- implement Exec
- implement BuildHooksEnv
- redefine HPATH and HTYPE
- - optionally redefine their run requirements (REQ_CLUSTER,
- REQ_MASTER); note that all commands require root permissions
+ - optionally redefine their run requirements:
+ REQ_MASTER: the LU needs to run on the master node
+ REQ_WSSTORE: the LU needs a writable SimpleStore
+
+ Note that all commands require root permissions.
"""
HPATH = None
HTYPE = None
_OP_REQP = []
- REQ_CLUSTER = True
REQ_MASTER = True
+ REQ_WSSTORE = False
def __init__(self, processor, op, cfg, sstore):
"""Constructor for LogicalUnit.
if attr_val is None:
raise errors.OpPrereqError("Required parameter '%s' missing" %
attr_name)
- if self.REQ_CLUSTER:
- if not cfg.IsCluster():
- raise errors.OpPrereqError("Cluster not initialized yet,"
- " use 'gnt-cluster init' first.")
- if self.REQ_MASTER:
- master = sstore.GetMasterNode()
- if master != utils.HostInfo().name:
- raise errors.OpPrereqError("Commands must be run on the master"
- " node %s" % master)
+
+ if not cfg.IsCluster():
+ raise errors.OpPrereqError("Cluster not initialized yet,"
+ " use 'gnt-cluster init' first.")
+ if self.REQ_MASTER:
+ master = sstore.GetMasterNode()
+ if master != utils.HostInfo().name:
+ raise errors.OpPrereqError("Commands must be run on the master"
+ " node %s" % master)
def __GetSSH(self):
"""Returns the SshRunner object
HTYPE = None
-def _AddHostToEtcHosts(hostname):
- """Wrapper around utils.SetEtcHostsEntry.
-
- """
- hi = utils.HostInfo(name=hostname)
- utils.SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
-
-
-def _RemoveHostFromEtcHosts(hostname):
- """Wrapper around utils.RemoveEtcHostsEntry.
-
- """
- hi = utils.HostInfo(name=hostname)
- utils.RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
- utils.RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
-
-
def _GetWantedNodes(lu, nodes):
"""Returns list of checked and expanded node names.
return _BuildInstanceHookEnv(**args)
-def _HasValidVG(vglist, vgname):
- """Checks if the volume group list is valid.
-
- A non-None return value means there's an error, and the return value
- is the error message.
-
- """
- vgsize = vglist.get(vgname, None)
- if vgsize is None:
- return "volume group '%s' missing" % vgname
- elif vgsize < 20480:
- return ("volume group '%s' too small (20480MiB required, %dMib found)" %
- (vgname, vgsize))
- return None
-
-
-def _InitSSHSetup(node):
- """Setup the SSH configuration for the cluster.
-
-
- This generates a dsa keypair for root, adds the pub key to the
- permitted hosts and adds the hostkey to its own known hosts.
-
- Args:
- node: the name of this host as a fqdn
-
- """
- priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
-
- for name in priv_key, pub_key:
- if os.path.exists(name):
- utils.CreateBackup(name)
- utils.RemoveFile(name)
-
- result = utils.RunCmd(["ssh-keygen", "-t", "dsa",
- "-f", priv_key,
- "-q", "-N", ""])
- if result.failed:
- raise errors.OpExecError("Could not generate ssh keypair, error %s" %
- result.output)
-
- f = open(pub_key, 'r')
- try:
- utils.AddAuthorizedKey(auth_keys, f.read(8192))
- finally:
- f.close()
-
-
-def _InitGanetiServerSetup(ss):
- """Setup the necessary configuration for the initial node daemon.
-
- This creates the nodepass file containing the shared password for
- the cluster and also generates the SSL certificate.
-
- """
- # Create pseudo random password
- randpass = sha.new(os.urandom(64)).hexdigest()
- # and write it into sstore
- ss.SetKey(ss.SS_NODED_PASS, randpass)
-
- result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
- "-days", str(365*5), "-nodes", "-x509",
- "-keyout", constants.SSL_CERT_FILE,
- "-out", constants.SSL_CERT_FILE, "-batch"])
- if result.failed:
- raise errors.OpExecError("could not generate server ssl cert, command"
- " %s had exitcode %s and error message %s" %
- (result.cmd, result.exit_code, result.output))
-
- os.chmod(constants.SSL_CERT_FILE, 0400)
-
- result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
-
- if result.failed:
- raise errors.OpExecError("Could not start the node daemon, command %s"
- " had exitcode %s and error %s" %
- (result.cmd, result.exit_code, result.output))
-
-
def _CheckInstanceBridgesExist(instance):
"""Check that the brigdes needed by an instance exist.
(brlist, instance.primary_node))
-class LUInitCluster(LogicalUnit):
- """Initialise the cluster.
-
- """
- HPATH = "cluster-init"
- HTYPE = constants.HTYPE_CLUSTER
- _OP_REQP = ["cluster_name", "hypervisor_type", "mac_prefix",
- "def_bridge", "master_netdev", "file_storage_dir"]
- REQ_CLUSTER = False
-
- def BuildHooksEnv(self):
- """Build hooks env.
-
- Notes: Since we don't require a cluster, we must manually add
- ourselves in the post-run node list.
-
- """
- env = {"OP_TARGET": self.op.cluster_name}
- return env, [], [self.hostname.name]
-
- def CheckPrereq(self):
- """Verify that the passed name is a valid one.
-
- """
- if config.ConfigWriter.IsCluster():
- raise errors.OpPrereqError("Cluster is already initialised")
-
- if self.op.hypervisor_type == constants.HT_XEN_HVM31:
- if not os.path.exists(constants.VNC_PASSWORD_FILE):
- raise errors.OpPrereqError("Please prepare the cluster VNC"
- "password file %s" %
- constants.VNC_PASSWORD_FILE)
-
- self.hostname = hostname = utils.HostInfo()
-
- if hostname.ip.startswith("127."):
- raise errors.OpPrereqError("This host's IP resolves to the private"
- " range (%s). Please fix DNS or %s." %
- (hostname.ip, constants.ETC_HOSTS))
-
- if not utils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT,
- source=constants.LOCALHOST_IP_ADDRESS):
- raise errors.OpPrereqError("Inconsistency: this host's name resolves"
- " to %s,\nbut this ip address does not"
- " belong to this host."
- " Aborting." % hostname.ip)
-
- self.clustername = clustername = utils.HostInfo(self.op.cluster_name)
-
- if utils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
- timeout=5):
- raise errors.OpPrereqError("Cluster IP already active. Aborting.")
-
- secondary_ip = getattr(self.op, "secondary_ip", None)
- if secondary_ip and not utils.IsValidIP(secondary_ip):
- raise errors.OpPrereqError("Invalid secondary ip given")
- if (secondary_ip and
- secondary_ip != hostname.ip and
- (not utils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
- source=constants.LOCALHOST_IP_ADDRESS))):
- raise errors.OpPrereqError("You gave %s as secondary IP,"
- " but it does not belong to this host." %
- secondary_ip)
- self.secondary_ip = secondary_ip
-
- 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 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):
- raise errors.OpPrereqError("Invalid mac prefix given '%s'" %
- self.op.mac_prefix)
-
- if self.op.hypervisor_type not in constants.HYPER_TYPES:
- raise errors.OpPrereqError("Invalid hypervisor type given '%s'" %
- self.op.hypervisor_type)
-
- result = utils.RunCmd(["ip", "link", "show", "dev", self.op.master_netdev])
- if result.failed:
- raise errors.OpPrereqError("Invalid master netdev given (%s): '%s'" %
- (self.op.master_netdev,
- result.output.strip()))
-
- if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
- os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
- raise errors.OpPrereqError("Init.d script '%s' missing or not"
- " executable." % constants.NODE_INITD_SCRIPT)
-
- def Exec(self, feedback_fn):
- """Initialize the cluster.
-
- """
- clustername = self.clustername
- hostname = self.hostname
-
- # set up the simple store
- self.sstore = ss = ssconf.SimpleStore()
- ss.SetKey(ss.SS_HYPERVISOR, self.op.hypervisor_type)
- ss.SetKey(ss.SS_MASTER_NODE, hostname.name)
- 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)
- ss.SetKey(ss.SS_CONFIG_VERSION, constants.CONFIG_VERSION)
-
- # set up the inter-node password and certificate
- _InitGanetiServerSetup(ss)
-
- # start the master ip
- rpc.call_node_start_master(hostname.name)
-
- # set up ssh config and /etc/hosts
- f = open(constants.SSH_HOST_RSA_PUB, 'r')
- try:
- sshline = f.read()
- finally:
- f.close()
- sshkey = sshline.split(" ")[1]
-
- _AddHostToEtcHosts(hostname.name)
- _InitSSHSetup(hostname.name)
-
- # init of cluster config file
- self.cfg = cfgw = config.ConfigWriter()
- cfgw.InitConfig(hostname.name, hostname.ip, self.secondary_ip,
- 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.
(node,))
bad = True
else:
- vgstatus = _HasValidVG(vglist, self.cfg.GetVGName())
+ vgstatus = utils.CheckVolumeGroupSize(vglist, self.cfg.GetVGName(),
+ constants.MIN_VG_SIZE)
if vgstatus:
feedback_fn(" - ERROR: %s on node %s" % (vgstatus, node))
bad = True
HPATH = "cluster-rename"
HTYPE = constants.HTYPE_CLUSTER
_OP_REQP = ["name"]
+ REQ_WSSTORE = True
def BuildHooksEnv(self):
"""Build hooks env.
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)
+ vgstatus = utils.CheckVolumeGroupSize(vglist[node], self.op.vg_name,
+ constants.MIN_VG_SIZE)
if vgstatus:
raise errors.OpPrereqError("Error on node '%s': %s" %
(node, vgstatus))
self.cfg.RemoveNode(node.name)
- _RemoveHostFromEtcHosts(node.name)
+ utils.RemoveHostFromEtcHosts(node.name)
class LUQueryNodes(NoHooksLU):
raise errors.OpExecError("Cannot transfer ssh keys to the new node")
# Add node to our /etc/hosts, and add key to known_hosts
- _AddHostToEtcHosts(new_node.name)
+ utils.AddHostToEtcHosts(new_node.name)
if new_node.secondary_ip != new_node.primary_ip:
if not rpc.call_node_tcp_ping(new_node.name,
HPATH = "master-failover"
HTYPE = constants.HTYPE_CLUSTER
REQ_MASTER = False
+ REQ_WSSTORE = True
_OP_REQP = []
def BuildHooksEnv(self):
instance.primary_node = target_node
# distribute new instance config to the other nodes
- self.cfg.AddInstance(instance)
+ self.cfg.Update(instance)
# Only start the instance if it's marked as up
if instance.status == "up":
info = nodeinfo.get(node, None)
if not info:
raise errors.OpPrereqError("Cannot get current information"
- " from node '%s'" % nodeinfo)
+ " from node '%s'" % node)
vg_free = info.get('vg_free', None)
if not isinstance(vg_free, int):
raise errors.OpPrereqError("Can't compute free disk space on"
" destination node '%s'" %
(self.op.bridge, pnode.name))
+ # memory check on primary node
+ if self.op.start:
+ _CheckNodeFreeMemory(self.cfg, self.pnode.name,
+ "creating instance %s" % self.op.instance_name,
+ self.op.mem_size)
+
# hvm_cdrom_image_path verification
if self.op.hvm_cdrom_image_path is not None:
if not os.path.isabs(self.op.hvm_cdrom_image_path):
"""
instance = self.instance
+
+ # Activate the instance disks if we're replacing them on a down instance
+ if instance.status == "down":
+ op = opcodes.OpActivateInstanceDisks(instance_name=instance.name)
+ self.proc.ChainOpCode(op)
+
if instance.disk_template == constants.DT_DRBD8:
if self.op.remote_node is None:
fn = self._ExecD8DiskOnly
fn = self._ExecD8Secondary
else:
raise errors.ProgrammerError("Unhandled disk replacement case")
- return fn(feedback_fn)
+
+ ret = fn(feedback_fn)
+
+ # Deactivate the instance disks if we're replacing them on a down instance
+ if instance.status == "down":
+ op = opcodes.OpDeactivateInstanceDisks(instance_name=instance.name)
+ self.proc.ChainOpCode(op)
+
+ return ret
class LUQueryInstanceData(NoHooksLU):