X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/714ff97c649e03f0269037dba7b29b6c04d825cb..57c7bc57723715fb20c46cf83337558d90c28054:/lib/backend.py diff --git a/lib/backend.py b/lib/backend.py index 4deaaad..ed60340 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -60,6 +60,8 @@ from ganeti import ssconf from ganeti import serializer from ganeti import netutils from ganeti import runtime +from ganeti import mcpu +from ganeti import compat _BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id" @@ -249,28 +251,93 @@ def GetMasterInfo(): master_netmask) -def ActivateMasterIp(): - """Activate the IP address of the master daemon. +def RunLocalHooks(hook_opcode, hooks_path, env_builder_fn): + """Decorator that runs hooks before and after the decorated function. + + @type hook_opcode: string + @param hook_opcode: opcode of the hook + @type hooks_path: string + @param hooks_path: path of the hooks + @type env_builder_fn: function + @param env_builder_fn: function that returns a dictionary containing the + environment variables for the hooks. Will get all the parameters of the + decorated function. + @raise RPCFail: in case of pre-hook failure """ - # GetMasterInfo will raise an exception if not able to return data - master_netdev, master_ip, _, family, master_netmask = GetMasterInfo() + def decorator(fn): + def wrapper(*args, **kwargs): + _, myself = ssconf.GetMasterAndMyself() + nodes = ([myself], [myself]) # these hooks run locally + + env_fn = compat.partial(env_builder_fn, *args, **kwargs) + + cfg = _GetConfig() + hr = HooksRunner() + hm = mcpu.HooksMaster(hook_opcode, hooks_path, nodes, hr.RunLocalHooks, + None, env_fn, logging.warning, cfg.GetClusterName(), + cfg.GetMasterNode()) + + hm.RunPhase(constants.HOOKS_PHASE_PRE) + result = fn(*args, **kwargs) + hm.RunPhase(constants.HOOKS_PHASE_POST) + + return result + return wrapper + return decorator + + +def _BuildMasterIpEnv(master_params, use_external_mip_script=None): + """Builds environment variables for master IP hooks. + + @type master_params: L{objects.MasterNetworkParameters} + @param master_params: network parameters of the master + @type use_external_mip_script: boolean + @param use_external_mip_script: whether to use an external master IP + address setup script (unused, but necessary per the implementation of the + _RunLocalHooks decorator) + + """ + # pylint: disable=W0613 + ver = netutils.IPAddress.GetVersionFromAddressFamily(master_params.ip_family) + env = { + "MASTER_NETDEV": master_params.netdev, + "MASTER_IP": master_params.ip, + "MASTER_NETMASK": master_params.netmask, + "CLUSTER_IP_VERSION": str(ver), + } + + return env + + +@RunLocalHooks(constants.FAKE_OP_MASTER_TURNUP, "master-ip-turnup", + _BuildMasterIpEnv) +def ActivateMasterIp(master_params, use_external_mip_script): + """Activate the IP address of the master daemon. + @type master_params: L{objects.MasterNetworkParameters} + @param master_params: network parameters of the master + @type use_external_mip_script: boolean + @param use_external_mip_script: whether to use an external master IP + address setup script + + """ + # pylint: disable=W0613 err_msg = None - if netutils.TcpPing(master_ip, constants.DEFAULT_NODED_PORT): - if netutils.IPAddress.Own(master_ip): + if netutils.TcpPing(master_params.ip, constants.DEFAULT_NODED_PORT): + if netutils.IPAddress.Own(master_params.ip): # we already have the ip: logging.debug("Master IP already configured, doing nothing") else: err_msg = "Someone else has the master ip, not activating" logging.error(err_msg) else: - ipcls = netutils.IPAddress.GetClassFromIpFamily(family) + ipcls = netutils.IPAddress.GetClassFromIpFamily(master_params.ip_family) result = utils.RunCmd([constants.IP_COMMAND_PATH, "address", "add", - "%s/%s" % (master_ip, master_netmask), - "dev", master_netdev, "label", - "%s:0" % master_netdev]) + "%s/%s" % (master_params.ip, master_params.netmask), + "dev", master_params.netdev, "label", + "%s:0" % master_params.netdev]) if result.failed: err_msg = "Can't activate master IP: %s" % result.output logging.error(err_msg) @@ -278,11 +345,12 @@ def ActivateMasterIp(): else: # we ignore the exit code of the following cmds if ipcls == netutils.IP4Address: - utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev, "-s", - master_ip, master_ip]) + utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_params.netdev, + "-s", master_params.ip, master_params.ip]) elif ipcls == netutils.IP6Address: try: - utils.RunCmd(["ndisc6", "-q", "-r 3", master_ip, master_netdev]) + utils.RunCmd(["ndisc6", "-q", "-r 3", master_params.ip, + master_params.netdev]) except errors.OpExecError: # TODO: Better error reporting logging.warning("Can't execute ndisc6, please install if missing") @@ -319,19 +387,25 @@ def StartMasterDaemons(no_voting): _Fail(msg) -def DeactivateMasterIp(): +@RunLocalHooks(constants.FAKE_OP_MASTER_TURNDOWN, "master-ip-turndown", + _BuildMasterIpEnv) +def DeactivateMasterIp(master_params, use_external_mip_script): """Deactivate the master IP on this node. + @type master_params: L{objects.MasterNetworkParameters} + @param master_params: network parameters of the master + @type use_external_mip_script: boolean + @param use_external_mip_script: whether to use an external master IP + address setup script + """ + # pylint: disable=W0613 # TODO: log and report back to the caller the error failures; we # need to decide in which case we fail the RPC for this - # GetMasterInfo will raise an exception if not able to return data - master_netdev, master_ip, _, _, master_netmask = GetMasterInfo() - result = utils.RunCmd([constants.IP_COMMAND_PATH, "address", "del", - "%s/%s" % (master_ip, master_netmask), - "dev", master_netdev]) + "%s/%s" % (master_params.ip, master_params.netmask), + "dev", master_params.netdev]) if result.failed: logging.error("Can't remove the master IP, error: %s", result.output) # but otherwise ignore the failure @@ -355,27 +429,35 @@ def StopMasterDaemons(): result.cmd, result.exit_code, result.output) -def ChangeMasterNetmask(netmask): +def ChangeMasterNetmask(old_netmask, netmask, master_ip, master_netdev): """Change the netmask of the master IP. + @param old_netmask: the old value of the netmask + @param netmask: the new value of the netmask + @param master_ip: the master IP + @param master_netdev: the master network device + """ - master_netdev, master_ip, _, _, old_netmask = GetMasterInfo() if old_netmask == netmask: return + if not netutils.IPAddress.Own(master_ip): + _Fail("The master IP address is not up, not attempting to change its" + " netmask") + result = utils.RunCmd([constants.IP_COMMAND_PATH, "address", "add", "%s/%s" % (master_ip, netmask), "dev", master_netdev, "label", "%s:0" % master_netdev]) if result.failed: - _Fail("Could not change the master IP netmask") + _Fail("Could not set the new netmask on the master IP address") result = utils.RunCmd([constants.IP_COMMAND_PATH, "address", "del", "%s/%s" % (master_ip, old_netmask), "dev", master_netdev, "label", "%s:0" % master_netdev]) if result.failed: - _Fail("Could not change the master IP netmask") + _Fail("Could not bring down the master IP address with the old netmask") def EtcHostsModify(mode, host, ip): @@ -597,6 +679,11 @@ def VerifyNode(what, cluster_name): result[constants.NV_MASTERIP] = netutils.TcpPing(master_ip, port, source=source) + if constants.NV_USERSCRIPTS in what: + result[constants.NV_USERSCRIPTS] = \ + [script for script in what[constants.NV_USERSCRIPTS] + if not (os.path.exists(script) and os.access(script, os.X_OK))] + if constants.NV_OOB_PATHS in what: result[constants.NV_OOB_PATHS] = tmp = [] for path in what[constants.NV_OOB_PATHS]: @@ -2722,7 +2809,10 @@ def JobQueueRename(old, new): _EnsureJobQueueFile(old) _EnsureJobQueueFile(new) - utils.RenameFile(old, new, mkdir=True) + getents = runtime.GetEnts() + + utils.RenameFile(old, new, mkdir=True, mkdir_mode=0700, + dir_uid=getents.masterd_uid, dir_gid=getents.masterd_gid) def BlockdevClose(instance_name, disks): @@ -3422,6 +3512,20 @@ class HooksRunner(object): # constant self._BASE_DIR = hooks_base_dir # pylint: disable=C0103 + def RunLocalHooks(self, node_list, hpath, phase, env): + """Check that the hooks will be run only locally and then run them. + + """ + assert len(node_list) == 1 + node = node_list[0] + _, myself = ssconf.GetMasterAndMyself() + assert node == myself + + results = self.RunHooks(hpath, phase, env) + + # Return values in the form expected by HooksMaster + return {node: (None, False, results)} + def RunHooks(self, hpath, phase, env): """Run the scripts in the hooks directory.