X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/411f8ad0a08f322069d1c9e9f035fbceb885c73f..c840ae6f4eb7ada3d5a2bcc95873e2bcd0a3f313:/lib/cmdlib.py diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 15c1e14..3d61d9d 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -74,6 +74,8 @@ class LogicalUnit(object): self.op = op self.cfg = cfg self.sstore = sstore + self.__ssh = None + for attr_name in self._OP_REQP: attr_val = getattr(op, attr_name, None) if attr_val is None: @@ -89,6 +91,16 @@ class LogicalUnit(object): raise errors.OpPrereqError("Commands must be run on the master" " node %s" % master) + def __GetSSH(self): + """Returns the SshRunner object + + """ + if not self.__ssh: + self.__ssh = ssh.SshRunner(self.sstore) + return self.__ssh + + ssh = property(fget=__GetSSH) + def CheckPrereq(self): """Check prerequisites for this LU. @@ -304,85 +316,6 @@ def _BuildInstanceHookEnvByObject(instance, override=None): return _BuildInstanceHookEnv(**args) -def _UpdateKnownHosts(fullnode, ip, pubkey): - """Ensure a node has a correct known_hosts entry. - - Args: - fullnode - Fully qualified domain name of host. (str) - ip - IPv4 address of host (str) - pubkey - the public key of the cluster - - """ - if os.path.exists(constants.SSH_KNOWN_HOSTS_FILE): - f = open(constants.SSH_KNOWN_HOSTS_FILE, 'r+') - else: - f = open(constants.SSH_KNOWN_HOSTS_FILE, 'w+') - - inthere = False - - save_lines = [] - add_lines = [] - removed = False - - for rawline in f: - logger.Debug('read %s' % (repr(rawline),)) - - parts = rawline.rstrip('\r\n').split() - - # Ignore unwanted lines - if len(parts) >= 3 and not rawline.lstrip()[0] == '#': - fields = parts[0].split(',') - key = parts[2] - - haveall = True - havesome = False - for spec in [ ip, fullnode ]: - if spec not in fields: - haveall = False - if spec in fields: - havesome = True - - logger.Debug("key, pubkey = %s." % (repr((key, pubkey)),)) - if haveall and key == pubkey: - inthere = True - save_lines.append(rawline) - logger.Debug("Keeping known_hosts '%s'." % (repr(rawline),)) - continue - - if havesome and (not haveall or key != pubkey): - removed = True - logger.Debug("Discarding known_hosts '%s'." % (repr(rawline),)) - continue - - save_lines.append(rawline) - - if not inthere: - add_lines.append('%s,%s ssh-rsa %s\n' % (fullnode, ip, pubkey)) - logger.Debug("Adding known_hosts '%s'." % (repr(add_lines[-1]),)) - - if removed: - save_lines = save_lines + add_lines - - # Write a new file and replace old. - fd, tmpname = tempfile.mkstemp('.tmp', 'known_hosts.', - constants.DATA_DIR) - newfile = os.fdopen(fd, 'w') - try: - newfile.write(''.join(save_lines)) - finally: - newfile.close() - logger.Debug("Wrote new known_hosts.") - os.rename(tmpname, constants.SSH_KNOWN_HOSTS_FILE) - - elif add_lines: - # Simply appending a new line will do the trick. - f.seek(0, 2) - for add in add_lines: - f.write(add) - - f.close() - - def _HasValidVG(vglist, vgname): """Checks if the volume group list is valid. @@ -480,8 +413,8 @@ class LUInitCluster(LogicalUnit): """ HPATH = "cluster-init" HTYPE = constants.HTYPE_CLUSTER - _OP_REQP = ["cluster_name", "hypervisor_type", "vg_name", "mac_prefix", - "def_bridge", "master_netdev"] + _OP_REQP = ["cluster_name", "hypervisor_type", "mac_prefix", + "def_bridge", "master_netdev", "file_storage_dir"] REQ_CLUSTER = False def BuildHooksEnv(self): @@ -539,11 +472,32 @@ class LUInitCluster(LogicalUnit): secondary_ip) self.secondary_ip = secondary_ip - # checks presence of the volume group given - vgstatus = _HasValidVG(utils.ListVolumeGroups(), self.op.vg_name) + if not hasattr(self.op, "vg_name"): + self.op.vg_name = None + # if vg_name not None, checks if volume group is valid + if self.op.vg_name: + vgstatus = _HasValidVG(utils.ListVolumeGroups(), self.op.vg_name) + if vgstatus: + raise errors.OpPrereqError("Error: %s\nspecify --no-lvm-storage if" + " you are not using lvm" % vgstatus) + + self.op.file_storage_dir = os.path.normpath(self.op.file_storage_dir) + + if not os.path.isabs(self.op.file_storage_dir): + raise errors.OpPrereqError("The file storage directory you have is" + " not an absolute path.") + + if not os.path.exists(self.op.file_storage_dir): + try: + os.makedirs(self.op.file_storage_dir, 0750) + except OSError, err: + raise errors.OpPrereqError("Cannot create file storage directory" + " '%s': %s" % + (self.op.file_storage_dir, err)) - if vgstatus: - raise errors.OpPrereqError("Error: %s" % vgstatus) + if not os.path.isdir(self.op.file_storage_dir): + raise errors.OpPrereqError("The file storage directory '%s' is not" + " a directory." % self.op.file_storage_dir) if not re.match("^[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}$", self.op.mac_prefix): @@ -579,6 +533,7 @@ class LUInitCluster(LogicalUnit): ss.SetKey(ss.SS_MASTER_IP, clustername.ip) ss.SetKey(ss.SS_MASTER_NETDEV, self.op.master_netdev) ss.SetKey(ss.SS_CLUSTER_NAME, clustername.name) + ss.SetKey(ss.SS_FILE_STORAGE_DIR, self.op.file_storage_dir) # set up the inter-node password and certificate _InitGanetiServerSetup(ss) @@ -595,9 +550,6 @@ class LUInitCluster(LogicalUnit): sshkey = sshline.split(" ")[1] _AddHostToEtcHosts(hostname.name) - - _UpdateKnownHosts(hostname.name, hostname.ip, sshkey) - _InitSSHSetup(hostname.name) # init of cluster config file @@ -606,6 +558,8 @@ class LUInitCluster(LogicalUnit): sshkey, self.op.mac_prefix, self.op.vg_name, self.op.def_bridge) + ssh.WriteKnownHostsFile(cfgw, ss, constants.SSH_KNOWN_HOSTS_FILE) + class LUDestroyCluster(NoHooksLU): """Logical unit for destroying the cluster. @@ -670,7 +624,7 @@ class LUVerifyCluster(NoHooksLU): # compares ganeti version local_version = constants.PROTOCOL_VERSION if not remote_version: - feedback_fn(" - ERROR: connection to %s failed" % (node)) + feedback_fn(" - ERROR: connection to %s failed" % (node)) return True if local_version != remote_version: @@ -1045,6 +999,79 @@ class LURenameCluster(LogicalUnit): " please restart manually.") +def _RecursiveCheckIfLVMBased(disk): + """Check if the given disk or its children are lvm-based. + + Args: + disk: ganeti.objects.Disk object + + Returns: + boolean indicating whether a LD_LV dev_type was found or not + + """ + if disk.children: + for chdisk in disk.children: + if _RecursiveCheckIfLVMBased(chdisk): + return True + return disk.dev_type == constants.LD_LV + + +class LUSetClusterParams(LogicalUnit): + """Change the parameters of the cluster. + + """ + HPATH = "cluster-modify" + HTYPE = constants.HTYPE_CLUSTER + _OP_REQP = [] + + def BuildHooksEnv(self): + """Build hooks env. + + """ + env = { + "OP_TARGET": self.sstore.GetClusterName(), + "NEW_VG_NAME": self.op.vg_name, + } + mn = self.sstore.GetMasterNode() + return env, [mn], [mn] + + def CheckPrereq(self): + """Check prerequisites. + + This checks whether the given params don't conflict and + if the given volume group is valid. + + """ + if not self.op.vg_name: + instances = [self.cfg.GetInstanceInfo(name) + for name in self.cfg.GetInstanceList()] + for inst in instances: + for disk in inst.disks: + if _RecursiveCheckIfLVMBased(disk): + raise errors.OpPrereqError("Cannot disable lvm storage while" + " lvm-based instances exist") + + # if vg_name not None, checks given volume group on all nodes + if self.op.vg_name: + node_list = self.cfg.GetNodeList() + vglist = rpc.call_vg_list(node_list) + for node in node_list: + vgstatus = _HasValidVG(vglist[node], self.op.vg_name) + if vgstatus: + raise errors.OpPrereqError("Error on node '%s': %s" % + (node, vgstatus)) + + def Exec(self, feedback_fn): + """Change the parameters of the cluster. + + """ + if self.op.vg_name != self.cfg.GetVGName(): + self.cfg.SetVGName(self.op.vg_name) + else: + feedback_fn("Cluster LVM configuration already in desired" + " state, not changing") + + def _WaitForSync(cfgw, instance, proc, oneshot=False, unlock=False): """Sleep and poll for an instance's disk to sync. @@ -1127,7 +1154,7 @@ def _CheckDiskConsistency(cfgw, dev, node, on_primary, ldisk=False): if on_primary or dev.AssembleOnSecondary(): rstats = rpc.call_blockdev_find(node, dev) if not rstats: - logger.ToStderr("Can't get any data from node %s" % node) + logger.ToStderr("Node %s: Disk degraded, not found or node down" % node) result = False else: result = result and (not rstats[idx]) @@ -1229,7 +1256,7 @@ class LURemoveNode(LogicalUnit): rpc.call_node_leave_cluster(node.name) - ssh.SSHCall(node.name, 'root', "%s stop" % constants.NODE_INITD_SCRIPT) + self.ssh.Run(node.name, 'root', "%s stop" % constants.NODE_INITD_SCRIPT) logger.Info("Removing node %s from config" % node.name) @@ -1539,7 +1566,7 @@ class LUAddNode(LogicalUnit): constants.SSL_CERT_FILE, gntpem, constants.NODE_INITD_SCRIPT)) - result = ssh.SSHCall(node, 'root', mycommand, batch=False, ask_key=True) + result = self.ssh.Run(node, 'root', mycommand, batch=False, ask_key=True) if result.failed: raise errors.OpExecError("Remote command on node %s, error: %s," " output: %s" % @@ -1584,9 +1611,6 @@ class LUAddNode(LogicalUnit): # Add node to our /etc/hosts, and add key to known_hosts _AddHostToEtcHosts(new_node.name) - _UpdateKnownHosts(new_node.name, new_node.primary_ip, - self.cfg.GetHostKey()) - if new_node.secondary_ip != new_node.primary_ip: if not rpc.call_node_tcp_ping(new_node.name, constants.LOCALHOST_IP_ADDRESS, @@ -1597,7 +1621,7 @@ class LUAddNode(LogicalUnit): " you gave (%s). Please fix and re-run this" " command." % new_node.secondary_ip) - success, msg = ssh.VerifyNodeHostname(node) + success, msg = self.ssh.VerifyNodeHostname(node) if not success: raise errors.OpExecError("Node '%s' claims it has a different hostname" " than the one the resolver gives: %s." @@ -1623,7 +1647,7 @@ class LUAddNode(LogicalUnit): if self.sstore.GetHypervisorType() == constants.HT_XEN_HVM31: to_copy.append(constants.VNC_PASSWORD_FILE) for fname in to_copy: - if not ssh.CopyFileToNode(node, fname): + if not self.ssh.CopyFileToNode(node, fname): logger.Error("could not copy file %s to node %s" % (fname, node)) logger.Info("adding node %s to cluster.conf" % node) @@ -1767,7 +1791,7 @@ class LUClusterCopyFile(NoHooksLU): for node in self.nodes: if node == myname: continue - if not ssh.CopyFileToNode(node, filename): + if not self.ssh.CopyFileToNode(node, filename): logger.Error("Copy of file %s to node %s failed" % (filename, node)) @@ -1808,9 +1832,15 @@ class LURunClusterCommand(NoHooksLU): """Run a command on some nodes. """ + # put the master at the end of the nodes list + master_node = self.sstore.GetMasterNode() + if master_node in self.nodes: + self.nodes.remove(master_node) + self.nodes.append(master_node) + data = [] for node in self.nodes: - result = ssh.SSHCall(node, "root", self.op.command) + result = self.ssh.Run(node, "root", self.op.command) data.append((node, result.output, result.exit_code)) return data @@ -2063,6 +2093,8 @@ class LUStartupInstance(LogicalUnit): force = self.op.force extra_args = getattr(self.op, "extra_args", "") + self.cfg.MarkInstanceUp(instance.name) + node_current = instance.primary_node _StartInstanceDisks(self.cfg, instance, force) @@ -2071,8 +2103,6 @@ class LUStartupInstance(LogicalUnit): _ShutdownInstanceDisks(instance, self.cfg) raise errors.OpExecError("Could not start instance") - self.cfg.MarkInstanceUp(instance.name) - class LURebootInstance(LogicalUnit): """Reboot an instance. @@ -2188,10 +2218,10 @@ class LUShutdownInstance(LogicalUnit): """ instance = self.instance node_current = instance.primary_node + self.cfg.MarkInstanceDown(instance.name) if not rpc.call_instance_shutdown(node_current, instance): logger.Error("could not shutdown instance") - self.cfg.MarkInstanceDown(instance.name) _ShutdownInstanceDisks(instance, self.cfg) @@ -2339,11 +2369,33 @@ class LURenameInstance(LogicalUnit): inst = self.instance old_name = inst.name + if inst.disk_template == constants.DT_FILE: + old_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1]) + self.cfg.RenameInstance(inst.name, self.op.new_name) # re-read the instance from the configuration after rename inst = self.cfg.GetInstanceInfo(self.op.new_name) + if inst.disk_template == constants.DT_FILE: + new_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1]) + result = rpc.call_file_storage_dir_rename(inst.primary_node, + old_file_storage_dir, + new_file_storage_dir) + + if not result: + raise errors.OpExecError("Could not connect to node '%s' to rename" + " directory '%s' to '%s' (but the instance" + " has been renamed in Ganeti)" % ( + inst.primary_node, old_file_storage_dir, + new_file_storage_dir)) + + if not result[0]: + raise errors.OpExecError("Could not rename directory '%s' to '%s'" + " (but the instance has been renamed in" + " Ganeti)" % (old_file_storage_dir, + new_file_storage_dir)) + _StartInstanceDisks(self.cfg, inst, None) try: if not rpc.call_instance_run_rename(inst.primary_node, inst, old_name, @@ -2604,7 +2656,7 @@ class LUFailoverInstance(LogicalUnit): for dev in instance.disks: # for remote_raid1, these are md over drbd if not _CheckDiskConsistency(self.cfg, dev, target_node, False): - if not self.op.ignore_consistency: + if instance.status == "up" and not self.op.ignore_consistency: raise errors.OpExecError("Disk %s is degraded on target node," " aborting failover." % dev.iv_name) @@ -2629,21 +2681,23 @@ class LUFailoverInstance(LogicalUnit): # distribute new instance config to the other nodes self.cfg.AddInstance(instance) - feedback_fn("* activating the instance's disks on target node") - logger.Info("Starting instance %s on node %s" % - (instance.name, target_node)) + # Only start the instance if it's marked as up + if instance.status == "up": + feedback_fn("* activating the instance's disks on target node") + logger.Info("Starting instance %s on node %s" % + (instance.name, target_node)) - disks_ok, dummy = _AssembleInstanceDisks(instance, self.cfg, - ignore_secondaries=True) - if not disks_ok: - _ShutdownInstanceDisks(instance, self.cfg) - raise errors.OpExecError("Can't activate the instance's disks") + disks_ok, dummy = _AssembleInstanceDisks(instance, self.cfg, + ignore_secondaries=True) + if not disks_ok: + _ShutdownInstanceDisks(instance, self.cfg) + raise errors.OpExecError("Can't activate the instance's disks") - feedback_fn("* starting the instance on the target node") - if not rpc.call_instance_start(target_node, instance, None): - _ShutdownInstanceDisks(instance, self.cfg) - raise errors.OpExecError("Could not start instance %s on node %s." % - (instance.name, target_node)) + feedback_fn("* starting the instance on the target node") + if not rpc.call_instance_start(target_node, instance, None): + _ShutdownInstanceDisks(instance, self.cfg) + raise errors.OpExecError("Could not start instance %s on node %s." % + (instance.name, target_node)) def _CreateBlockDevOnPrimary(cfg, node, instance, device, info): @@ -2744,7 +2798,8 @@ def _GenerateDRBD8Branch(cfg, primary, secondary, size, names, iv_name): def _GenerateDiskTemplate(cfg, template_name, instance_name, primary_node, - secondary_nodes, disk_sz, swap_sz): + secondary_nodes, disk_sz, swap_sz, + file_storage_dir, file_driver): """Generate the entire disk layout for a given template type. """ @@ -2765,43 +2820,6 @@ def _GenerateDiskTemplate(cfg, template_name, logical_id=(vgname, names[1]), iv_name = "sdb") disks = [sda_dev, sdb_dev] - elif template_name == constants.DT_LOCAL_RAID1: - if len(secondary_nodes) != 0: - raise errors.ProgrammerError("Wrong template configuration") - - - names = _GenerateUniqueNames(cfg, [".sda_m1", ".sda_m2", - ".sdb_m1", ".sdb_m2"]) - sda_dev_m1 = objects.Disk(dev_type=constants.LD_LV, size=disk_sz, - logical_id=(vgname, names[0])) - sda_dev_m2 = objects.Disk(dev_type=constants.LD_LV, size=disk_sz, - logical_id=(vgname, names[1])) - md_sda_dev = objects.Disk(dev_type=constants.LD_MD_R1, iv_name = "sda", - size=disk_sz, - children = [sda_dev_m1, sda_dev_m2]) - sdb_dev_m1 = objects.Disk(dev_type=constants.LD_LV, size=swap_sz, - logical_id=(vgname, names[2])) - sdb_dev_m2 = objects.Disk(dev_type=constants.LD_LV, size=swap_sz, - logical_id=(vgname, names[3])) - md_sdb_dev = objects.Disk(dev_type=constants.LD_MD_R1, iv_name = "sdb", - size=swap_sz, - children = [sdb_dev_m1, sdb_dev_m2]) - disks = [md_sda_dev, md_sdb_dev] - elif template_name == constants.DT_REMOTE_RAID1: - if len(secondary_nodes) != 1: - raise errors.ProgrammerError("Wrong template configuration") - remote_node = secondary_nodes[0] - names = _GenerateUniqueNames(cfg, [".sda_data", ".sda_meta", - ".sdb_data", ".sdb_meta"]) - drbd_sda_dev = _GenerateMDDRBDBranch(cfg, primary_node, remote_node, - disk_sz, names[0:2]) - md_sda_dev = objects.Disk(dev_type=constants.LD_MD_R1, iv_name="sda", - children = [drbd_sda_dev], size=disk_sz) - drbd_sdb_dev = _GenerateMDDRBDBranch(cfg, primary_node, remote_node, - swap_sz, names[2:4]) - md_sdb_dev = objects.Disk(dev_type=constants.LD_MD_R1, iv_name="sdb", - children = [drbd_sdb_dev], size=swap_sz) - disks = [md_sda_dev, md_sdb_dev] elif template_name == constants.DT_DRBD8: if len(secondary_nodes) != 1: raise errors.ProgrammerError("Wrong template configuration") @@ -2813,6 +2831,17 @@ def _GenerateDiskTemplate(cfg, template_name, drbd_sdb_dev = _GenerateDRBD8Branch(cfg, primary_node, remote_node, swap_sz, names[2:4], "sdb") disks = [drbd_sda_dev, drbd_sdb_dev] + elif template_name == constants.DT_FILE: + if len(secondary_nodes) != 0: + raise errors.ProgrammerError("Wrong template configuration") + + file_sda_dev = objects.Disk(dev_type=constants.LD_FILE, size=disk_sz, + iv_name="sda", logical_id=(file_driver, + "%s/sda" % file_storage_dir)) + file_sdb_dev = objects.Disk(dev_type=constants.LD_FILE, size=swap_sz, + iv_name="sdb", logical_id=(file_driver, + "%s/sdb" % file_storage_dir)) + disks = [file_sda_dev, file_sdb_dev] else: raise errors.ProgrammerError("Invalid disk template '%s'" % template_name) return disks @@ -2839,9 +2868,22 @@ def _CreateDisks(cfg, instance): """ info = _GetInstanceInfoText(instance) + if instance.disk_template == constants.DT_FILE: + file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1]) + result = rpc.call_file_storage_dir_create(instance.primary_node, + file_storage_dir) + + if not result: + logger.Error("Could not connect to node '%s'" % inst.primary_node) + return False + + if not result[0]: + logger.Error("failed to create directory '%s'" % file_storage_dir) + return False + for device in instance.disks: logger.Info("creating volume %s for instance %s" % - (device.iv_name, instance.name)) + (device.iv_name, instance.name)) #HARDCODE for secondary_node in instance.secondary_nodes: if not _CreateBlockDevOnSecondary(cfg, secondary_node, instance, @@ -2855,6 +2897,7 @@ def _CreateDisks(cfg, instance): logger.Error("failed to create volume %s on primary!" % device.iv_name) return False + return True @@ -2884,6 +2927,14 @@ def _RemoveDisks(instance, cfg): " continuing anyway" % (device.iv_name, node)) result = False + + if instance.disk_template == constants.DT_FILE: + file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1]) + if not rpc.call_file_storage_dir_remove(instance.primary_node, + file_storage_dir): + logger.Error("could not remove directory '%s'" % file_storage_dir) + result = False + return result @@ -2942,6 +2993,11 @@ class LUCreateInstance(LogicalUnit): raise errors.OpPrereqError("Invalid instance creation mode '%s'" % self.op.mode) + if (not self.cfg.GetVGName() and + self.op.disk_template not in constants.DTS_NOT_LVM): + raise errors.OpPrereqError("Cluster does not support lvm-based" + " instances") + if self.op.mode == constants.INSTANCE_IMPORT: src_node = getattr(self.op, "src_node", None) src_path = getattr(self.op, "src_path", None) @@ -2994,6 +3050,15 @@ class LUCreateInstance(LogicalUnit): if self.op.disk_template not in constants.DISK_TEMPLATES: raise errors.OpPrereqError("Invalid disk template name") + if (self.op.file_driver and + not self.op.file_driver in constants.FILE_DRIVER): + raise errors.OpPrereqError("Invalid file driver name '%s'" % + self.op.file_driver) + + if self.op.file_storage_dir and os.path.isabs(self.op.file_storage_dir): + raise errors.OpPrereqError("File storage directory not a relative" + " path") + if self.op.disk_template in constants.DTS_NET_MIRROR: if getattr(self.op, "snode", None) is None: raise errors.OpPrereqError("The networked disk templates need" @@ -3012,10 +3077,9 @@ class LUCreateInstance(LogicalUnit): req_size_dict = { constants.DT_DISKLESS: None, constants.DT_PLAIN: self.op.disk_size + self.op.swap_size, - constants.DT_LOCAL_RAID1: (self.op.disk_size + self.op.swap_size) * 2, # 256 MB are added for drbd metadata, 128MB for each drbd device - constants.DT_REMOTE_RAID1: self.op.disk_size + self.op.swap_size + 256, constants.DT_DRBD8: self.op.disk_size + self.op.swap_size + 256, + constants.DT_FILE: None, } if self.op.disk_template not in req_size_dict: @@ -3132,11 +3196,25 @@ class LUCreateInstance(LogicalUnit): else: network_port = None + # this is needed because os.path.join does not accept None arguments + if self.op.file_storage_dir is None: + string_file_storage_dir = "" + else: + string_file_storage_dir = self.op.file_storage_dir + + # build the full file storage dir path + file_storage_dir = os.path.normpath(os.path.join( + self.sstore.GetFileStorageDir(), + string_file_storage_dir, instance)) + + disks = _GenerateDiskTemplate(self.cfg, self.op.disk_template, instance, pnode_name, self.secondaries, self.op.disk_size, - self.op.swap_size) + self.op.swap_size, + file_storage_dir, + self.op.file_driver) iobj = objects.Instance(name=instance, os=self.op.os_type, primary_node=pnode_name, @@ -3249,214 +3327,9 @@ class LUConnectConsole(NoHooksLU): hyper = hypervisor.GetHypervisor() console_cmd = hyper.GetShellCommandForConsole(instance) - # build ssh cmdline - argv = ["ssh", "-q", "-t"] - argv.extend(ssh.KNOWN_HOSTS_OPTS) - argv.extend(ssh.BATCH_MODE_OPTS) - argv.append(node) - argv.append(console_cmd) - return "ssh", argv - - -class LUAddMDDRBDComponent(LogicalUnit): - """Adda new mirror member to an instance's disk. - - """ - HPATH = "mirror-add" - HTYPE = constants.HTYPE_INSTANCE - _OP_REQP = ["instance_name", "remote_node", "disk_name"] - - def BuildHooksEnv(self): - """Build hooks env. - - This runs on the master, the primary and all the secondaries. - - """ - env = { - "NEW_SECONDARY": self.op.remote_node, - "DISK_NAME": self.op.disk_name, - } - env.update(_BuildInstanceHookEnvByObject(self.instance)) - nl = [self.sstore.GetMasterNode(), self.instance.primary_node, - self.op.remote_node,] + list(self.instance.secondary_nodes) - return env, nl, nl - - def CheckPrereq(self): - """Check prerequisites. - - This checks that the instance is in the cluster. - - """ - instance = self.cfg.GetInstanceInfo( - self.cfg.ExpandInstanceName(self.op.instance_name)) - if instance is None: - raise errors.OpPrereqError("Instance '%s' not known" % - self.op.instance_name) - self.instance = instance - - remote_node = self.cfg.ExpandNodeName(self.op.remote_node) - if remote_node is None: - raise errors.OpPrereqError("Node '%s' not known" % self.op.remote_node) - self.remote_node = remote_node - - if remote_node == instance.primary_node: - raise errors.OpPrereqError("The specified node is the primary node of" - " the instance.") - - if instance.disk_template != constants.DT_REMOTE_RAID1: - raise errors.OpPrereqError("Instance's disk layout is not" - " remote_raid1.") - for disk in instance.disks: - if disk.iv_name == self.op.disk_name: - break - else: - raise errors.OpPrereqError("Can't find this device ('%s') in the" - " instance." % self.op.disk_name) - if len(disk.children) > 1: - raise errors.OpPrereqError("The device already has two slave devices." - " This would create a 3-disk raid1 which we" - " don't allow.") - self.disk = disk - - def Exec(self, feedback_fn): - """Add the mirror component - - """ - disk = self.disk - instance = self.instance - - remote_node = self.remote_node - lv_names = [".%s_%s" % (disk.iv_name, suf) for suf in ["data", "meta"]] - names = _GenerateUniqueNames(self.cfg, lv_names) - new_drbd = _GenerateMDDRBDBranch(self.cfg, instance.primary_node, - remote_node, disk.size, names) - - logger.Info("adding new mirror component on secondary") - #HARDCODE - if not _CreateBlockDevOnSecondary(self.cfg, remote_node, instance, - new_drbd, False, - _GetInstanceInfoText(instance)): - raise errors.OpExecError("Failed to create new component on secondary" - " node %s" % remote_node) - - logger.Info("adding new mirror component on primary") - #HARDCODE - if not _CreateBlockDevOnPrimary(self.cfg, instance.primary_node, - instance, new_drbd, - _GetInstanceInfoText(instance)): - # remove secondary dev - self.cfg.SetDiskID(new_drbd, remote_node) - rpc.call_blockdev_remove(remote_node, new_drbd) - raise errors.OpExecError("Failed to create volume on primary") - - # the device exists now - # call the primary node to add the mirror to md - logger.Info("adding new mirror component to md") - if not rpc.call_blockdev_addchildren(instance.primary_node, - disk, [new_drbd]): - logger.Error("Can't add mirror compoment to md!") - self.cfg.SetDiskID(new_drbd, remote_node) - if not rpc.call_blockdev_remove(remote_node, new_drbd): - logger.Error("Can't rollback on secondary") - self.cfg.SetDiskID(new_drbd, instance.primary_node) - if not rpc.call_blockdev_remove(instance.primary_node, new_drbd): - logger.Error("Can't rollback on primary") - raise errors.OpExecError("Can't add mirror component to md array") - - disk.children.append(new_drbd) - - self.cfg.AddInstance(instance) - - _WaitForSync(self.cfg, instance, self.proc) - - return 0 - - -class LURemoveMDDRBDComponent(LogicalUnit): - """Remove a component from a remote_raid1 disk. - - """ - HPATH = "mirror-remove" - HTYPE = constants.HTYPE_INSTANCE - _OP_REQP = ["instance_name", "disk_name", "disk_id"] - - def BuildHooksEnv(self): - """Build hooks env. - - This runs on the master, the primary and all the secondaries. - - """ - env = { - "DISK_NAME": self.op.disk_name, - "DISK_ID": self.op.disk_id, - "OLD_SECONDARY": self.old_secondary, - } - env.update(_BuildInstanceHookEnvByObject(self.instance)) - nl = [self.sstore.GetMasterNode(), - self.instance.primary_node] + list(self.instance.secondary_nodes) - return env, nl, nl - - def CheckPrereq(self): - """Check prerequisites. - - This checks that the instance is in the cluster. - """ - instance = self.cfg.GetInstanceInfo( - self.cfg.ExpandInstanceName(self.op.instance_name)) - if instance is None: - raise errors.OpPrereqError("Instance '%s' not known" % - self.op.instance_name) - self.instance = instance - - if instance.disk_template != constants.DT_REMOTE_RAID1: - raise errors.OpPrereqError("Instance's disk layout is not" - " remote_raid1.") - for disk in instance.disks: - if disk.iv_name == self.op.disk_name: - break - else: - raise errors.OpPrereqError("Can't find this device ('%s') in the" - " instance." % self.op.disk_name) - for child in disk.children: - if (child.dev_type == constants.LD_DRBD7 and - child.logical_id[2] == self.op.disk_id): - break - else: - raise errors.OpPrereqError("Can't find the device with this port.") - - if len(disk.children) < 2: - raise errors.OpPrereqError("Cannot remove the last component from" - " a mirror.") - self.disk = disk - self.child = child - if self.child.logical_id[0] == instance.primary_node: - oid = 1 - else: - oid = 0 - self.old_secondary = self.child.logical_id[oid] - - def Exec(self, feedback_fn): - """Remove the mirror component - - """ - instance = self.instance - disk = self.disk - child = self.child - logger.Info("remove mirror component") - self.cfg.SetDiskID(disk, instance.primary_node) - if not rpc.call_blockdev_removechildren(instance.primary_node, - disk, [child]): - raise errors.OpExecError("Can't remove child from mirror.") - - for node in child.logical_id[:2]: - self.cfg.SetDiskID(child, node) - if not rpc.call_blockdev_remove(node, child): - logger.Error("Warning: failed to remove device from node %s," - " continuing operation." % node) - - disk.children.remove(child) - self.cfg.AddInstance(instance) + # build ssh cmdline + return self.ssh.BuildCmd(node, "root", console_cmd, batch=True, tty=True) class LUReplaceDisks(LogicalUnit): @@ -4121,7 +3994,7 @@ class LUQueryInstanceData(NoHooksLU): return result -class LUSetInstanceParms(LogicalUnit): +class LUSetInstanceParams(LogicalUnit): """Modifies an instances's parameters. """ @@ -4173,9 +4046,9 @@ class LUSetInstanceParms(LogicalUnit): self.kernel_path = getattr(self.op, "kernel_path", None) self.initrd_path = getattr(self.op, "initrd_path", None) self.hvm_boot_order = getattr(self.op, "hvm_boot_order", None) - all_parms = [self.mem, self.vcpus, self.ip, self.bridge, self.mac, - self.kernel_path, self.initrd_path, self.hvm_boot_order] - if all_parms.count(None) == len(all_parms): + all_params = [self.mem, self.vcpus, self.ip, self.bridge, self.mac, + self.kernel_path, self.initrd_path, self.hvm_boot_order] + if all_params.count(None) == len(all_params): raise errors.OpPrereqError("No changes submitted") if self.mem is not None: try: @@ -4358,10 +4231,11 @@ class LUExportInstance(LogicalUnit): instance = self.instance dst_node = self.dst_node src_node = instance.primary_node - # shutdown the instance, unless requested not to do so if self.op.shutdown: - op = opcodes.OpShutdownInstance(instance_name=instance.name) - self.proc.ChainOpCode(op) + # shutdown the instance, but not the disks + if not rpc.call_instance_shutdown(src_node, instance): + raise errors.OpExecError("Could not shutdown instance %s on node %s" % + (instance.name, source_node)) vgname = self.cfg.GetVGName() @@ -4384,22 +4258,20 @@ class LUExportInstance(LogicalUnit): snap_disks.append(new_dev) finally: - if self.op.shutdown: - op = opcodes.OpStartupInstance(instance_name=instance.name, - force=False) - self.proc.ChainOpCode(op) + if self.op.shutdown and instance.status == "up": + if not rpc.call_instance_start(src_node, instance, None): + _ShutdownInstanceDisks(instance, self.cfg) + raise errors.OpExecError("Could not start instance") # TODO: check for size for dev in snap_disks: - if not rpc.call_snapshot_export(src_node, dev, dst_node.name, - instance): - logger.Error("could not export block device %s from node" - " %s to node %s" % - (dev.logical_id[1], src_node, dst_node.name)) + if not rpc.call_snapshot_export(src_node, dev, dst_node.name, instance): + logger.Error("could not export block device %s from node %s to node %s" + % (dev.logical_id[1], src_node, dst_node.name)) if not rpc.call_blockdev_remove(src_node, dev): - logger.Error("could not remove snapshot block device %s from" - " node %s" % (dev.logical_id[1], src_node)) + logger.Error("could not remove snapshot block device %s from node %s" % + (dev.logical_id[1], src_node)) if not rpc.call_finalize_export(dst_node.name, instance, snap_disks): logger.Error("could not finalize export for instance %s on node %s" %