summaryrefslogblamecommitdiffstats
path: root/inc/trigger.inc.php
blob: df06229b1d6d7215836b22cd392363cc387066c5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                                    
 


                                                                                
          
                                                                            
           
                                                          
         

                                          
                                           
                                                   
                                           
                                                              


                                                                 
                                               

                                             





                                                                        

                                      
         
 
           


                                                                                            


                                                                                                          
           
                                                         
         





                                                                          
                                    
                                     
                                                                
                                                      
                                     




                                                               

                                                                                                                            
                                                      
                                            












                                                                                 
                                                                      
                                    

                                                                                   
                                                                       
                                    
                 
                             


           

                                              
                                                                                                    
                                                                                                   
                                                                        
           
                                                                                 
         


                                                                
                                        
                                    




                                                           
                                           
                                                    
                                          
                                                  
                                                     
                                           
                        
                                     
                                       
                 

                                                                                            
                                    

                                                

                                     
                 
                                                                    

                                                   
                                                
                                                                                                                              

                                                                    
                   




                                                                                          
                                                                                
         
 
           
                                           

                                                                    
           
                                                     
         
                                   






                                                                                        
                         
                                                                                                 
                                                              
                                
                                                  
                         
                 
                                  
         
 
                                                                                  
         
                                                               
                                                       
                                                    






                                                           









                                                               


                               


                                                                              













                                                                          
 
 
<?php

/**
 * This is one giant class containing various functions that will generate
 * required config files, daemon instances and more, mostly through the Taskmanager.
 * Most function *should* only actually do something if it is required to do so.
 * eg. a "launchSomething" function should only launch something if it isn't already
 * running. Checking if something is running can happen in that very function, or in
 * a task that the function is calling.
 */
class Trigger
{

	/**
	 * Compile iPXE pxelinux menu. Needs to be done whenever the server's IP
	 * address changes.
	 *
	 * @return ?string false if launching task failed, task-id otherwise
	 */
	public static function ipxe(string $taskId = null)
	{
		static $lastResult = null;
		if ($lastResult !== null)
			return $lastResult;
		$hooks = Hook::load('ipxe-update');
		foreach ($hooks as $hook) {
			$ret = function($taskId) use ($hook) {
				$ret = include_once($hook->file);
				if (is_string($ret))
					return $ret;
				return $taskId;
			};
			$ret = $ret($taskId);
			if (is_string($ret)) {
				$taskId = $ret;
			} elseif (is_array($ret) && isset($ret['id'])) {
				$taskId = $ret['id'];
			}
		}
		$lastResult = $taskId;
		return $taskId;
	}

	/**
	 * Try to automatically determine the primary IP address of the server.
	 * This only works if the server has either one public IPv4 address (and potentially
	 * one or more non-public addresses), or one private address.
	 * 
	 * @return boolean true if current configured IP address is still valid, or if a new address could
	 * successfully be determined, false otherwise
	 */
	public static function autoUpdateServerIp(): bool
	{
		for ($i = 0; $i < 5; ++$i) {
			$task = Taskmanager::submit('LocalAddressesList');
			if ($task !== false)
				break;
			sleep(1);
		}
		if ($task === false)
			return false;
		$task = Taskmanager::waitComplete($task, 10000);
		if (empty($task['data']['addresses']))
			return false;

		$serverIp = Property::getServerIp();
		$publicCandidate = 'none';
		$privateCandidate = 'none';
		foreach ($task['data']['addresses'] as $addr) {
			if (substr($addr['ip'], 0, 4) === '127.' || preg_match('/^\d+\.\d+\.\d+\.\d+$/', $addr['ip']) !== 1)
				continue;
			if ($addr['ip'] === $serverIp)
				return true;
			if (Util::isPublicIpv4($addr['ip'])) {
				if ($publicCandidate === 'none')
					$publicCandidate = $addr['ip'];
				else
					$publicCandidate = 'many';
			} else {
				if ($privateCandidate === 'none')
					$privateCandidate = $addr['ip'];
				else
					$privateCandidate = 'many';
			}
		}
		if ($publicCandidate !== 'none' && $publicCandidate !== 'many') {
			Property::setServerIp($publicCandidate, true);
			return true;
		}
		if ($privateCandidate !== 'none' && $privateCandidate !== 'many') {
			Property::setServerIp($privateCandidate, true);
			return true;
		}
		return false;
	}

	/**
	 * Mount the VM store into the server.
	 *
	 * @param array|false $vmstore VM Store configuration to use. If false, read from properties
	 * @param bool $ifLocalOnly Only execute task if the storage type is local (used for DNBD3)
	 * @return ?string task id of mount procedure, or false on error
	 */
	public static function mount($vmstore = false, bool $ifLocalOnly = false)
	{
		if ($vmstore === false) {
			$vmstore = Property::getVmStoreConfig();
		}
		if (!is_array($vmstore))
			return null;
		if (isset($vmstore['storetype'])) {
			$storetype = $vmstore['storetype'];
		} else {
			$storetype = 'unknown';
		}
		if ($storetype === 'nfs') {
			$addr = $vmstore['nfsaddr'];
			$opts = 'nfsopts';
		} elseif ($storetype === 'cifs') {
			$addr = $vmstore['cifsaddr'];
			$opts = 'cifsopts';
		} else {
			$opts = null;
			$addr = 'null';
		}
		// Bail out if storage is not local, and we only want to run it in that case
		if ($ifLocalOnly && $addr !== 'null')
			return null;
		if (isset($vmstore[$opts])) {
			$opts = $vmstore[$opts];
		}else {
			$opts = null;
		}
		$status = Taskmanager::submit('MountVmStore', array(
				'address' => $addr,
				'type' => 'images',
				'opts' => $opts,
				'localNfs' => !Module::isAvailable('dnbd3') || !Dnbd3::isEnabled() || Dnbd3::hasNfsFallback(),
				'username' => $vmstore['cifsuser'],
				'password' => $vmstore['cifspasswd']
		));
		if (!Taskmanager::isFailed($status)) {
			// In case we have a concurrent active task, this should be enough
			// for the taskmanager to give us the existing id
			$status = Taskmanager::waitComplete($status, 100);
		}
		return $status['data']['existingTask'] ?? $status['id'] ?? null;
	}

	/**
	 * Check and process all callbacks.
	 * 
	 * @return boolean Whether there are still callbacks pending
	 */
	public static function checkCallbacks(): bool
	{
		$tasksLeft = false;
		$callbackList = TaskmanagerCallback::getPendingCallbacks();
		foreach ($callbackList as $taskid => $callbacks) {
			$status = Taskmanager::status($taskid);
			if ($status === false)
				continue;
			foreach ($callbacks as $callback) {
				TaskmanagerCallback::handleCallback($callback, $status);
			}
			if (Taskmanager::isFailed($status) || Taskmanager::isFinished($status)) {
				Taskmanager::release($status);
			} else {
				$tasksLeft = true;
			}
		}
		return $tasksLeft;
	}

	private static function triggerDaemons(string $action, $parent, &$taskids)
	{
		$task = Taskmanager::submit('Systemctl', array(
				'operation' => $action,
				'service' => 'dmsd',
				'parentTask' => $parent,
				'failOnParentFail' => false
		));
		if (isset($task['id'])) {
			$taskids['dmsdid'] = $task['id'];
			$parent = $task['id'];
		}
		$task = Taskmanager::submit('Systemctl', array(
			'operation' => $action,
			'service' => 'dnbd3-server',
			'parentTask' => $parent,
			'failOnParentFail' => false
		));
		if (isset($task['id'])) {
			$taskids['dnbd3id'] = $task['id'];
			$parent = $task['id'];
		}
		return $parent;
	}

	/**
	 * Stop any daemons that might be sitting on the VMstore, or database.
	 */
	public static function stopDaemons($parent, &$taskids)
	{
		$parent = self::triggerDaemons('stop', $parent, $taskids);
		$task = Taskmanager::submit('LdadpLauncher', array(
				'ids' => array(),
				'parentTask' => $parent,
				'failOnParentFail' => false
		));
		if (isset($task['id'])) {
			$taskids['ldadpid'] = $task['id'];
			$parent = $task['id'];
		}
		return $parent;
	}

}