All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aoindustries.aoserv.master.FailoverHandler Maven / Gradle / Ivy

There is a newer version: 1.91.8
Show newest version
/*
 * aoserv-master - Master server for the AOServ Platform.
 * Copyright (C) 2001-2013, 2015, 2017, 2018, 2019, 2020  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-master.
 *
 * aoserv-master is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aoserv-master is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoserv-master.  If not, see .
 */
package com.aoindustries.aoserv.master;

import com.aoindustries.aoserv.client.account.Account;
import com.aoindustries.aoserv.client.backup.FileReplicationLog;
import com.aoindustries.aoserv.client.linux.Server;
import com.aoindustries.aoserv.client.schema.Table;
import com.aoindustries.aoserv.daemon.client.AOServDaemonConnector;
import com.aoindustries.aoserv.daemon.client.AOServDaemonProtocol;
import com.aoindustries.collections.IntList;
import com.aoindustries.cron.CronDaemon;
import com.aoindustries.cron.CronJob;
import com.aoindustries.cron.Schedule;
import com.aoindustries.dbc.DatabaseConnection;
import com.aoindustries.io.BitRateProvider;
import com.aoindustries.io.stream.StreamableOutput;
import com.aoindustries.net.HostAddress;
import com.aoindustries.util.Tuple2;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The FailoverHandler handles all the accesses to the failover tables.
 *
 * @author  AO Industries, Inc.
 */
final public class FailoverHandler implements CronJob {

	private static final Logger logger = Logger.getLogger(FailoverHandler.class.getName());

	public static int addFileReplicationLog(
		DatabaseConnection conn,
		RequestSource source,
		InvalidateList invalidateList,
		int fileReplication,
		long startTime,
		long endTime,
		int scanned,
		int updated,
		long bytes,
		boolean isSuccessful
	) throws IOException, SQLException {
		//String mustring = source.getUsername();
		//User mu = MasterServer.getUser(conn, mustring);
		//if (mu==null) throw new SQLException("User "+mustring+" is not master user and may not access backup.FileReplicationLog.");

		// The server must be an exact package match to allow adding log entries
		int host = getFromHostForFileReplication(conn, fileReplication);
		Account.Name userPackage = AccountUserHandler.getPackageForUser(conn, source.getCurrentAdministrator());
		Account.Name serverPackage = PackageHandler.getNameForPackage(conn, NetHostHandler.getPackageForHost(conn, host));
		if(!userPackage.equals(serverPackage)) throw new SQLException("userPackage!=serverPackage: may only set backup.FileReplicationLog for servers that have the same package as the business_administrator adding the log entry");
		//ServerHandler.checkAccessServer(conn, source, "addFileReplicationLog", server);

		int fileReplicationLog = conn.executeIntUpdate(
			"INSERT INTO\n"
			+ "  backup.\"FileReplicationLog\"\n"
			+ "VALUES (\n"
			+ "  default,\n"
			+ "  ?,\n"
			+ "  ?,\n"
			+ "  ?,\n"
			+ "  ?,\n"
			+ "  ?,\n"
			+ "  ?,\n"
			+ "  ?\n"
			+ ") RETURNING id",
			fileReplication,
			new Timestamp(startTime),
			new Timestamp(endTime),
			scanned,
			updated,
			bytes,
			isSuccessful
		);

		// Notify all clients of the update
		invalidateList.addTable(
			conn,
			Table.TableID.FAILOVER_FILE_LOG,
			NetHostHandler.getAccountsForHost(conn, host),
			host,
			false
		);
		return fileReplicationLog;
	}

	public static void setFileReplicationBitRate(
		DatabaseConnection conn,
		RequestSource source,
		InvalidateList invalidateList,
		int fileReplication,
		Long bitRate
	) throws IOException, SQLException {
		if(
			bitRate!=null
			&& bitRate hours,
		List minutes
	) throws IOException, SQLException {
		// The server must be an exact package match to allow setting the schedule
		int host = getFromHostForFileReplication(conn, fileReplication);
		Account.Name userPackage = AccountUserHandler.getPackageForUser(conn, source.getCurrentAdministrator());
		Account.Name serverPackage = PackageHandler.getNameForPackage(conn, NetHostHandler.getPackageForHost(conn, host));
		if(!userPackage.equals(serverPackage)) throw new SQLException("userPackage!=serverPackage: may only set backup.FileReplicationSchedule for servers that have the same package as the business_administrator setting the schedule");

		// If not modified, invalidation will not be performed
		boolean modified = false;

		// Get the list of all the ids that currently exist
		IntList ids = conn.executeIntListQuery("select id from backup.\"FileReplicationSchedule\" where replication=?", fileReplication);
		int size = hours.size();
		for(int c=0;c paths,
		List backupEnableds,
		List requireds
	) throws IOException, SQLException {
		// The server must be an exact package match to allow setting the schedule
		int host = getFromHostForFileReplication(conn, fileReplication);
		Account.Name userPackage = AccountUserHandler.getPackageForUser(conn, source.getCurrentAdministrator());
		Account.Name serverPackage = PackageHandler.getNameForPackage(conn, NetHostHandler.getPackageForHost(conn, host));
		if(!userPackage.equals(serverPackage)) throw new SQLException("userPackage!=serverPackage: may only set backup.FileReplicationSetting for servers that have the same package as the business_administrator making the settings");

		// If not modified, invalidation will not be performed
		boolean modified = false;

		// Get the list of all the ids that currently exist
		IntList ids = conn.executeIntListQuery("select id from backup.\"FileReplicationSetting\" where replication=?", fileReplication);
		int size = paths.size();
		for(int c=0;c CursorMode.AUTO_CURSOR_ABOVE ? CursorMode.FETCH : CursorMode.SELECT,
			new FileReplicationLog(),
			"select * from backup.\"FileReplicationLog\" where replication=? order by start_time desc limit ?",
			fileReplication,
			maxRows
		);
	}

	public static Tuple2 getFileReplicationActivity(
		DatabaseConnection conn,
		RequestSource source,
		int fileReplication
	) throws IOException, SQLException {
		// Check access for the from server
		int fromHost = getFromHostForFileReplication(conn, fileReplication);
		NetHostHandler.checkAccessHost(conn, source, "getFileReplicationActivity", fromHost);

		// Find where going
		int backupPartition = FailoverHandler.getBackupPartitionForFileReplication(conn, fileReplication);
		int toLinuxServer = BackupHandler.getLinuxServerForBackupPartition(conn, backupPartition);

		// Contact the server
		AOServDaemonConnector daemonConnector = DaemonHandler.getDaemonConnector(conn, toLinuxServer);
		conn.releaseConnection();
		return daemonConnector.getFailoverFileReplicationActivity(fileReplication);
	}

	/**
	 * Runs at 1:20 am daily.
	 */
	private static final Schedule schedule = (minute, hour, dayOfMonth, month, dayOfWeek, year) -> minute==45 && hour==1;

	@Override
	public Schedule getSchedule() {
		return schedule;
	}

	@Override
	public int getThreadPriority() {
		return Thread.NORM_PRIORITY-1;
	}

	private static boolean started=false;

	public static void start() {
		synchronized(System.out) {
			if(!started) {
				System.out.print("Starting " + FailoverHandler.class.getSimpleName() + ": ");
				CronDaemon.addCronJob(new FailoverHandler(), logger);
				started=true;
				System.out.println("Done");
			}
		}
	}

	private FailoverHandler() {
	}

	@Override
	public void run(int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year) {
		try {
			MasterDatabase.getDatabase().executeUpdate("delete from backup.\"FileReplicationLog\" where end_time <= (now()-'1 year'::interval)");
		} catch(RuntimeException | IOException | SQLException T) {
			logger.log(Level.SEVERE, null, T);
		}
	}

	public static Server.DaemonAccess requestReplicationDaemonAccess(
		DatabaseConnection conn,
		RequestSource source,
		int fileReplication
	) throws IOException, SQLException {
		// Security checks
		//String username=source.getUsername();
		//User masterUser=MasterServer.getUser(conn, username);
		//if(masterUser==null) throw new SQLException("Only master users allowed to request daemon access.");
		// Sometime later we might restrict certain command codes to certain users

		// Current user must have the same exact package as the from server
		Account.Name userPackage = AccountUserHandler.getPackageForUser(conn, source.getCurrentAdministrator());
		int fromServer=FailoverHandler.getFromHostForFileReplication(conn, fileReplication);
		Account.Name serverPackage = PackageHandler.getNameForPackage(conn, NetHostHandler.getPackageForHost(conn, fromServer));
		if(!userPackage.equals(serverPackage)) throw new SQLException("account.Administrator.username.package!=servers.package.name: Not allowed to request daemon access for FAILOVER_FILE_REPLICATION");
		//ServerHandler.checkAccessServer(conn, source, "requestDaemonAccess", fromServer);

		// The to server must match server
		int backupPartition = FailoverHandler.getBackupPartitionForFileReplication(conn, fileReplication);
		int toServer = BackupHandler.getLinuxServerForBackupPartition(conn, backupPartition);

		// The overall backup path includes both the toPath and the server name
		String serverName;
		if(NetHostHandler.isLinuxServer(conn, fromServer)) {
			serverName = NetHostHandler.getHostnameForLinuxServer(conn, fromServer).toString();
		} else {
			serverName =
				PackageHandler.getNameForPackage(conn, NetHostHandler.getPackageForHost(conn, fromServer))
				+"/"
				+ NetHostHandler.getNameForHost(conn, fromServer)
			;
		}

		int quota_gid = conn.executeIntQuery("select coalesce(quota_gid, -1) from backup.\"FileReplication\" where id=?", fileReplication);

		// Verify that the backup_partition is the correct type
		boolean isQuotaEnabled = conn.executeBooleanQuery("select bp.quota_enabled from backup.\"FileReplication\" ffr inner join backup.\"BackupPartition\" bp on ffr.backup_partition=bp.id where ffr.id=?", fileReplication);
		if(quota_gid==-1) {
			if(isQuotaEnabled) throw new SQLException("quota_gid is null when quota_enabled=true: backup.FileReplication.id="+fileReplication);
		} else {
			if(!isQuotaEnabled) throw new SQLException("quota_gid is not null when quota_enabled=false: backup.FileReplication.id="+fileReplication);
		}

		HostAddress connectAddress = conn.executeObjectQuery(
			ObjectFactories.hostAddressFactory,
			"select connect_address from backup.\"FileReplication\" where id=?",
			fileReplication
		);
		return DaemonHandler.grantDaemonAccess(
			conn,
			toServer,
			connectAddress,
			AOServDaemonProtocol.FAILOVER_FILE_REPLICATION,
			Integer.toString(fileReplication),
			serverName,
			conn.executeStringQuery("select bp.path from backup.\"FileReplication\" ffr inner join backup.\"BackupPartition\" bp on ffr.backup_partition=bp.id where ffr.id=?", fileReplication),
			quota_gid==-1 ? null : Integer.toString(quota_gid)
		);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy