X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/55de1d683189dafb5ad04c8fc8bddae08893d206..f70bcabc5806897ad27215d6842035529008f8e1:/lib/cmdlib.py diff --git a/lib/cmdlib.py b/lib/cmdlib.py index bc5a70f..167dc03 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -40,6 +40,7 @@ import tempfile import shutil import itertools import operator +import ipaddr from ganeti import ssh from ganeti import utils @@ -60,6 +61,7 @@ from ganeti import opcodes from ganeti import ht from ganeti import rpc from ganeti import runtime +from ganeti import network import ganeti.masterd.instance # pylint: disable=W0611 @@ -815,7 +817,7 @@ def _GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False): # in a nicer way ipolicy[key] = list(value) try: - objects.InstancePolicy.CheckParameterSyntax(ipolicy) + objects.InstancePolicy.CheckParameterSyntax(ipolicy, not group_policy) except errors.ConfigurationError, err: raise errors.OpPrereqError("Invalid instance policy: %s" % err, errors.ECODE_INVAL) @@ -1336,7 +1338,7 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status, @type vcpus: string @param vcpus: the count of VCPUs the instance has @type nics: list - @param nics: list of tuples (ip, mac, mode, link) representing + @param nics: list of tuples (ip, mac, mode, link, network) representing the NICs the instance has @type disk_template: string @param disk_template: the disk template of the instance @@ -1371,13 +1373,14 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status, } if nics: nic_count = len(nics) - for idx, (ip, mac, mode, link) in enumerate(nics): + for idx, (ip, mac, mode, link, network) in enumerate(nics): if ip is None: ip = "" env["INSTANCE_NIC%d_IP" % idx] = ip env["INSTANCE_NIC%d_MAC" % idx] = mac env["INSTANCE_NIC%d_MODE" % idx] = mode env["INSTANCE_NIC%d_LINK" % idx] = link + env["INSTANCE_NIC%d_NETWORK" % idx] = network if mode == constants.NIC_MODE_BRIDGED: env["INSTANCE_NIC%d_BRIDGE" % idx] = link else: @@ -1427,7 +1430,8 @@ def _NICListToTuple(lu, nics): filled_params = cluster.SimpleFillNIC(nic.nicparams) mode = filled_params[constants.NIC_MODE] link = filled_params[constants.NIC_LINK] - hooks_nics.append((ip, mac, mode, link)) + network = nic.network + hooks_nics.append((ip, mac, mode, link, network)) return hooks_nics @@ -3214,10 +3218,12 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): if master_node not in self.my_node_info: additional_nodes.append(master_node) vf_node_info.append(self.all_node_info[master_node]) - # Add the first vm_capable node we find which is not included + # Add the first vm_capable node we find which is not included, + # excluding the master node (which we already have) for node in absent_nodes: nodeinfo = self.all_node_info[node] - if nodeinfo.vm_capable and not nodeinfo.offline: + if (nodeinfo.vm_capable and not nodeinfo.offline and + node != master_node): additional_nodes.append(node) vf_node_info.append(self.all_node_info[node]) break @@ -4310,6 +4316,9 @@ def _ComputeAncillaryFiles(cluster, redist): if cluster.modify_etc_hosts: files_all.add(constants.ETC_HOSTS) + if cluster.use_external_mip_script: + files_all.add(constants.EXTERNAL_MASTER_SETUP_SCRIPT) + # Files which are optional, these must: # - be present in one other category as well # - either exist or not exist on all nodes of that category (mc, vm all) @@ -4323,10 +4332,6 @@ def _ComputeAncillaryFiles(cluster, redist): if not redist: files_mc.add(constants.CLUSTER_CONF_FILE) - # FIXME: this should also be replicated but Ganeti doesn't support files_mc - # replication - files_mc.add(constants.DEFAULT_MASTER_SETUP_SCRIPT) - # Files which should only be on VM-capable nodes files_vm = set(filename for hv_name in cluster.enabled_hypervisors @@ -4367,7 +4372,8 @@ def _RedistributeAncillaryFiles(lu, additional_nodes=None, additional_vm=True): master_info = lu.cfg.GetNodeInfo(lu.cfg.GetMasterNode()) online_nodes = lu.cfg.GetOnlineNodeList() - vm_nodes = lu.cfg.GetVmCapableNodeList() + online_set = frozenset(online_nodes) + vm_nodes = list(online_set.intersection(lu.cfg.GetVmCapableNodeList())) if additional_nodes is not None: online_nodes.extend(additional_nodes) @@ -6374,10 +6380,12 @@ def _AssembleInstanceDisks(lu, instance, disks=None, ignore_secondaries=False, False, idx) msg = result.fail_msg if msg: + is_offline_secondary = (node in instance.secondary_nodes and + result.offline) lu.proc.LogWarning("Could not prepare block device %s on node %s" " (is_primary=False, pass=1): %s", inst_disk.iv_name, node, msg) - if not ignore_secondaries: + if not (ignore_secondaries or is_offline_secondary): disks_ok = False # FIXME: race condition on drbd migration to primary @@ -6983,9 +6991,6 @@ class LUInstanceReinstall(LogicalUnit): "Cannot retrieve locked instance %s" % self.op.instance_name _CheckNodeOnline(self, instance.primary_node, "Instance primary node" " offline, cannot reinstall") - for node in instance.secondary_nodes: - _CheckNodeOnline(self, node, "Instance secondary node offline," - " cannot reinstall") if instance.disk_template == constants.DT_DISKLESS: raise errors.OpPrereqError("Instance '%s' has no disks" % @@ -8884,6 +8889,7 @@ def _WipeDisks(lu, instance): result = lu.rpc.call_blockdev_pause_resume_sync(node, (instance.disks, instance), True) + result.Raise("Failed RPC to node %s for pausing the disk syncing" % node) for idx, success in enumerate(result.payload): if not success: @@ -8931,12 +8937,17 @@ def _WipeDisks(lu, instance): (instance.disks, instance), False) - for idx, success in enumerate(result.payload): - if not success: - lu.LogWarning("Resume sync of disk %d failed, please have a" - " look at the status and troubleshoot the issue", idx) - logging.warn("resume-sync of instance %s for disks %d failed", - instance.name, idx) + if result.fail_msg: + lu.LogWarning("RPC call to %s for resuming disk syncing failed," + " please have a look at the status and troubleshoot" + " the issue: %s", node, result.fail_msg) + else: + for idx, success in enumerate(result.payload): + if not success: + lu.LogWarning("Resume sync of disk %d failed, please have a" + " look at the status and troubleshoot the issue", idx) + logging.warn("resume-sync of instance %s for disks %d failed", + instance.name, idx) def _CreateDisks(lu, instance, to_skip=None, target_node=None): @@ -9013,11 +9024,12 @@ def _RemoveDisks(lu, instance, target_node=None, ignore_failures=False): edata = device.ComputeNodeTree(instance.primary_node) for node, disk in edata: lu.cfg.SetDiskID(disk, node) - msg = lu.rpc.call_blockdev_remove(node, disk).fail_msg - if msg: + result = lu.rpc.call_blockdev_remove(node, disk) + if result.fail_msg: lu.LogWarning("Could not remove disk %s on node %s," - " continuing anyway: %s", idx, node, msg) - all_result = False + " continuing anyway: %s", idx, node, result.fail_msg) + if not (result.offline and node != instance.primary_node): + all_result = False # if this is a DRBD disk, return its port to the pool if device.dev_type in constants.LDS_DRBD: @@ -9075,7 +9087,7 @@ def _ComputeDiskSizePerVG(disk_template, disks): def _ComputeDiskSize(disk_template, disks): - """Compute disk size requirements in the volume group + """Compute disk size requirements according to disk template """ # Required free disk space as a function of disk and swap space @@ -9085,10 +9097,10 @@ def _ComputeDiskSize(disk_template, disks): # 128 MB are added for drbd metadata for each disk constants.DT_DRBD8: sum(d[constants.IDISK_SIZE] + DRBD_META_SIZE for d in disks), - constants.DT_FILE: None, - constants.DT_SHARED_FILE: 0, + constants.DT_FILE: sum(d[constants.IDISK_SIZE] for d in disks), + constants.DT_SHARED_FILE: sum(d[constants.IDISK_SIZE] for d in disks), constants.DT_BLOCK: 0, - constants.DT_RBD: 0, + constants.DT_RBD: sum(d[constants.IDISK_SIZE] for d in disks), } if disk_template not in req_size_dict: @@ -9397,6 +9409,8 @@ class LUInstanceCreate(LogicalUnit): """Run the allocator based on input opcode. """ + #TODO Export network to iallocator so that it chooses a pnode + # in a nodegroup that has the desired network connected to nics = [n.ToDict() for n in self.nics] ial = IAllocator(self.cfg, self.rpc, mode=constants.IALLOCATOR_MODE_ALLOC, @@ -9730,14 +9744,19 @@ class LUInstanceCreate(LogicalUnit): if nic_mode is None or nic_mode == constants.VALUE_AUTO: nic_mode = cluster.nicparams[constants.PP_DEFAULT][constants.NIC_MODE] - # in routed mode, for the first nic, the default ip is 'auto' - if nic_mode == constants.NIC_MODE_ROUTED and idx == 0: - default_ip_mode = constants.VALUE_AUTO + net = nic.get(constants.INIC_NETWORK, None) + link = nic.get(constants.NIC_LINK, None) + ip = nic.get(constants.INIC_IP, None) + + if net is None or net.lower() == constants.VALUE_NONE: + net = None else: - default_ip_mode = constants.VALUE_NONE + if nic_mode_req is not None or link is not None: + raise errors.OpPrereqError("If network is given, no mode or link" + " is allowed to be passed", + errors.ECODE_INVAL) # ip validity checks - ip = nic.get(constants.INIC_IP, default_ip_mode) if ip is None or ip.lower() == constants.VALUE_NONE: nic_ip = None elif ip.lower() == constants.VALUE_AUTO: @@ -9747,9 +9766,18 @@ class LUInstanceCreate(LogicalUnit): errors.ECODE_INVAL) nic_ip = self.hostname1.ip else: - if not netutils.IPAddress.IsValid(ip): + # We defer pool operations until later, so that the iallocator has + # filled in the instance's node(s) dimara + if ip.lower() == constants.NIC_IP_POOL: + if net is None: + raise errors.OpPrereqError("if ip=pool, parameter network" + " must be passed too", + errors.ECODE_INVAL) + + elif not netutils.IPAddress.IsValid(ip): raise errors.OpPrereqError("Invalid IP address '%s'" % ip, errors.ECODE_INVAL) + nic_ip = ip # TODO: check the ip address for uniqueness @@ -9770,9 +9798,6 @@ class LUInstanceCreate(LogicalUnit): errors.ECODE_NOTUNIQUE) # Build nic parameters - link = nic.get(constants.INIC_LINK, None) - if link == constants.VALUE_AUTO: - link = cluster.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] nicparams = {} if nic_mode_req: nicparams[constants.NIC_MODE] = nic_mode @@ -9781,7 +9806,8 @@ class LUInstanceCreate(LogicalUnit): check_params = cluster.SimpleFillNIC(nicparams) objects.NIC.CheckParameterSyntax(check_params) - self.nics.append(objects.NIC(mac=mac, ip=nic_ip, nicparams=nicparams)) + self.nics.append(objects.NIC(mac=mac, ip=nic_ip, + network=net, nicparams=check_params)) # disk checks/pre-build default_vg = self.cfg.GetVGName() @@ -9884,6 +9910,45 @@ class LUInstanceCreate(LogicalUnit): self.secondaries = [] + # Fill in any IPs from IP pools. This must happen here, because we need to + # know the nic's primary node, as specified by the iallocator + for idx, nic in enumerate(self.nics): + net = nic.network + if net is not None: + netparams = self.cfg.GetGroupNetParams(net, self.pnode.name) + if netparams is None: + raise errors.OpPrereqError("No netparams found for network" + " %s. Propably not connected to" + " node's %s nodegroup" % + (net, self.pnode.name), + errors.ECODE_INVAL) + self.LogInfo("NIC/%d inherits netparams %s" % + (idx, netparams.values())) + nic.nicparams = dict(netparams) + if nic.ip is not None: + filled_params = cluster.SimpleFillNIC(nic.nicparams) + if nic.ip.lower() == constants.NIC_IP_POOL: + try: + nic.ip = self.cfg.GenerateIp(net, self.proc.GetECId()) + except errors.ReservationError: + raise errors.OpPrereqError("Unable to get a free IP for NIC %d" + " from the address pool" % idx, + errors.ECODE_STATE) + self.LogInfo("Chose IP %s from network %s", nic.ip, net) + else: + try: + self.cfg.ReserveIp(net, nic.ip, self.proc.GetECId()) + except errors.ReservationError: + raise errors.OpPrereqError("IP address %s already in use" + " or does not belong to network %s" % + (nic.ip, net), + errors.ECODE_NOTUNIQUE) + else: + # net is None, ip None or given + if self.op.conflicts_check: + _CheckForConflictingIp(self, nic.ip, self.pnode.name) + + # mirror node verification if self.op.disk_template in constants.DTS_INT_MIRROR: if self.op.snode == pnode.name: @@ -13627,7 +13692,7 @@ class LUGroupAdd(LogicalUnit): cluster = self.cfg.GetClusterInfo() full_ipolicy = cluster.SimpleFillIPolicy(self.op.ipolicy) try: - objects.InstancePolicy.CheckParameterSyntax(full_ipolicy) + objects.InstancePolicy.CheckParameterSyntax(full_ipolicy, False) except errors.ConfigurationError, err: raise errors.OpPrereqError("Invalid instance policy: %s" % err, errors.ECODE_INVAL) @@ -13966,7 +14031,7 @@ class LUGroupSetParams(LogicalUnit): if self.op.ndparams: new_ndparams = _GetUpdatedParams(self.group.ndparams, self.op.ndparams) - utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES) + utils.ForceDictType(new_ndparams, constants.NDS_PARAMETER_TYPES) self.new_ndparams = new_ndparams if self.op.diskparams: @@ -14919,6 +14984,7 @@ class IAllocator(object): "ip": nic.ip, "mode": filled_params[constants.NIC_MODE], "link": filled_params[constants.NIC_LINK], + "network": nic.network, } if filled_params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: nic_dict["bridge"] = filled_params[constants.NIC_LINK] @@ -15316,6 +15382,620 @@ class LUTestAllocator(NoHooksLU): result = ial.out_text return result +# Network LUs +class LUNetworkAdd(LogicalUnit): + """Logical unit for creating networks. + + """ + HPATH = "network-add" + HTYPE = constants.HTYPE_NETWORK + REQ_BGL = False + + def BuildHooksNodes(self): + """Build hooks nodes. + + """ + mn = self.cfg.GetMasterNode() + return ([mn], [mn]) + + def ExpandNames(self): + self.network_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId()) + self.needed_locks = {} + self.add_locks[locking.LEVEL_NETWORK] = self.network_uuid + + def CheckPrereq(self): + """Check prerequisites. + + This checks that the given group name is not an existing node group + already. + + """ + if self.op.network is None: + raise errors.OpPrereqError("Network must be given", + errors.ECODE_INVAL) + + uuid = self.cfg.LookupNetwork(self.op.network_name) + + if uuid: + raise errors.OpPrereqError("Network '%s' already defined" % + self.op.network, errors.ECODE_EXISTS) + + + def BuildHooksEnv(self): + """Build hooks env. + + """ + env = { + "NETWORK_NAME": self.op.network_name, + "NETWORK_SUBNET": self.op.network, + "NETWORK_GATEWAY": self.op.gateway, + "NETWORK_SUBNET6": self.op.network6, + "NETWORK_GATEWAY6": self.op.gateway6, + "NETWORK_MAC_PREFIX": self.op.mac_prefix, + "NETWORK_TYPE": self.op.network_type, + } + return env + + def Exec(self, feedback_fn): + """Add the ip pool to the cluster. + + """ + nobj = objects.Network(name=self.op.network_name, + network=self.op.network, + gateway=self.op.gateway, + network6=self.op.network6, + gateway6=self.op.gateway6, + mac_prefix=self.op.mac_prefix, + network_type=self.op.network_type, + uuid=self.network_uuid, + family=4) + # Initialize the associated address pool + try: + pool = network.AddressPool.InitializeNetwork(nobj) + except errors.AddressPoolError, e: + raise errors.OpExecError("Cannot create IP pool for this network. %s" % e) + + # Check if we need to reserve the nodes and the cluster master IP + # These may not be allocated to any instances in routed mode, as + # they wouldn't function anyway. + for node in self.cfg.GetAllNodesInfo().values(): + for ip in [node.primary_ip, node.secondary_ip]: + try: + pool.Reserve(ip) + self.LogInfo("Reserved node %s's IP (%s)", node.name, ip) + + except errors.AddressPoolError: + pass + + master_ip = self.cfg.GetClusterInfo().master_ip + try: + pool.Reserve(master_ip) + self.LogInfo("Reserved cluster master IP (%s)", master_ip) + except errors.AddressPoolError: + pass + + if self.op.add_reserved_ips: + for ip in self.op.add_reserved_ips: + try: + pool.Reserve(ip, external=True) + except errors.AddressPoolError, e: + raise errors.OpExecError("Cannot reserve IP %s. %s " % (ip, e)) + + self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False) + del self.remove_locks[locking.LEVEL_NETWORK] + + +class LUNetworkRemove(LogicalUnit): + HPATH = "network-remove" + HTYPE = constants.HTYPE_NETWORK + REQ_BGL = False + + def ExpandNames(self): + self.network_uuid = self.cfg.LookupNetwork(self.op.network_name) + + self.needed_locks = { + locking.LEVEL_NETWORK: [self.network_uuid], + } + + + def CheckPrereq(self): + """Check prerequisites. + + This checks that the given network name exists as a network, that is + empty (i.e., contains no nodes), and that is not the last group of the + cluster. + + """ + if not self.network_uuid: + raise errors.OpPrereqError("Network %s not found" % self.op.network_name, + errors.ECODE_INVAL) + + # Verify that the network is not conncted. + node_groups = [group.name + for group in self.cfg.GetAllNodeGroupsInfo().values() + for network in group.networks.keys() + if network == self.network_uuid] + + if node_groups: + self.LogWarning("Nework '%s' is connected to the following" + " node groups: %s" % (self.op.network_name, + utils.CommaJoin(utils.NiceSort(node_groups)))) + raise errors.OpPrereqError("Network still connected", + errors.ECODE_STATE) + + def BuildHooksEnv(self): + """Build hooks env. + + """ + return { + "NETWORK_NAME": self.op.network_name, + } + + def BuildHooksNodes(self): + """Build hooks nodes. + + """ + mn = self.cfg.GetMasterNode() + return ([mn], [mn]) + + def Exec(self, feedback_fn): + """Remove the network. + + """ + try: + self.cfg.RemoveNetwork(self.network_uuid) + except errors.ConfigurationError: + raise errors.OpExecError("Network '%s' with UUID %s disappeared" % + (self.op.network_name, self.network_uuid)) + + +class LUNetworkSetParams(LogicalUnit): + """Modifies the parameters of a network. + + """ + HPATH = "network-modify" + HTYPE = constants.HTYPE_NETWORK + REQ_BGL = False + + def CheckArguments(self): + if (self.op.gateway and + (self.op.add_reserved_ips or self.op.remove_reserved_ips)): + raise errors.OpPrereqError("Cannot modify gateway and reserved ips" + " at once", errors.ECODE_INVAL) + + + def ExpandNames(self): + self.network_uuid = self.cfg.LookupNetwork(self.op.network_name) + self.network = self.cfg.GetNetwork(self.network_uuid) + self.needed_locks = { + locking.LEVEL_NETWORK: [self.network_uuid], + } + + + if self.network is None: + raise errors.OpPrereqError("Could not retrieve network '%s' (UUID: %s)" % + (self.op.network_name, self.network_uuid), + errors.ECODE_INVAL) + + def CheckPrereq(self): + """Check prerequisites. + + """ + self.gateway = self.network.gateway + self.network_type = self.network.network_type + self.mac_prefix = self.network.mac_prefix + self.network6 = self.network.network6 + self.gateway6 = self.network.gateway6 + + self.pool = network.AddressPool(self.network) + + if self.op.gateway: + if self.op.gateway == constants.VALUE_NONE: + self.gateway = None + else: + self.gateway = self.op.gateway + if self.pool.IsReserved(self.gateway): + raise errors.OpPrereqError("%s is already reserved" % + self.gateway, errors.ECODE_INVAL) + + if self.op.network_type: + if self.op.network_type == constants.VALUE_NONE: + self.network_type = None + else: + self.network_type = self.op.network_type + + if self.op.mac_prefix: + if self.op.mac_prefix == constants.VALUE_NONE: + self.mac_prefix = None + else: + self.mac_prefix = self.op.mac_prefix + + if self.op.gateway6: + if self.op.gateway6 == constants.VALUE_NONE: + self.gateway6 = None + else: + self.gateway6 = self.op.gateway6 + + if self.op.network6: + if self.op.network6 == constants.VALUE_NONE: + self.network6 = None + else: + self.network6 = self.op.network6 + + + + def BuildHooksEnv(self): + """Build hooks env. + + """ + env = { + "NETWORK_NAME": self.op.network_name, + "NETWORK_SUBNET": self.network.network, + "NETWORK_GATEWAY": self.gateway, + "NETWORK_SUBNET6": self.network6, + "NETWORK_GATEWAY6": self.gateway6, + "NETWORK_MAC_PREFIX": self.mac_prefix, + "NETWORK_TYPE": self.network_type, + } + return env + + def BuildHooksNodes(self): + """Build hooks nodes. + + """ + mn = self.cfg.GetMasterNode() + return ([mn], [mn]) + + def Exec(self, feedback_fn): + """Modifies the network. + + """ + #TODO: reserve/release via temporary reservation manager + # extend cfg.ReserveIp/ReleaseIp with the external flag + if self.op.gateway: + if self.gateway == self.network.gateway: + self.LogWarning("Gateway is already %s" % self.gateway) + else: + if self.gateway: + self.pool.Reserve(self.gateway, external=True) + if self.network.gateway: + self.pool.Release(self.network.gateway, external=True) + self.network.gateway = self.gateway + + if self.op.add_reserved_ips: + for ip in self.op.add_reserved_ips: + try: + if self.pool.IsReserved(ip): + self.LogWarning("IP %s is already reserved" % ip) + else: + self.pool.Reserve(ip, external=True) + except errors.AddressPoolError, e: + self.LogWarning("Cannot reserve ip %s. %s" % (ip, e)) + + if self.op.remove_reserved_ips: + for ip in self.op.remove_reserved_ips: + if ip == self.network.gateway: + self.LogWarning("Cannot unreserve Gateway's IP") + continue + try: + if not self.pool.IsReserved(ip): + self.LogWarning("IP %s is already unreserved" % ip) + else: + self.pool.Release(ip, external=True) + except errors.AddressPoolError, e: + self.LogWarning("Cannot release ip %s. %s" % (ip, e)) + + if self.op.mac_prefix: + self.network.mac_prefix = self.mac_prefix + + if self.op.network6: + self.network.network6 = self.network6 + + if self.op.gateway6: + self.network.gateway6 = self.gateway6 + + if self.op.network_type: + self.network.network_type = self.network_type + + self.pool.Validate() + + self.cfg.Update(self.network, feedback_fn) + + +class _NetworkQuery(_QueryBase): + FIELDS = query.NETWORK_FIELDS + + def ExpandNames(self, lu): + lu.needed_locks = {} + + self._all_networks = lu.cfg.GetAllNetworksInfo() + name_to_uuid = dict((n.name, n.uuid) for n in self._all_networks.values()) + + if not self.names: + self.wanted = [name_to_uuid[name] + for name in utils.NiceSort(name_to_uuid.keys())] + else: + # Accept names to be either names or UUIDs. + missing = [] + self.wanted = [] + all_uuid = frozenset(self._all_networks.keys()) + + for name in self.names: + if name in all_uuid: + self.wanted.append(name) + elif name in name_to_uuid: + self.wanted.append(name_to_uuid[name]) + else: + missing.append(name) + + if missing: + raise errors.OpPrereqError("Some networks do not exist: %s" % missing, + errors.ECODE_NOENT) + + def DeclareLocks(self, lu, level): + pass + + def _GetQueryData(self, lu): + """Computes the list of networks and their attributes. + + """ + do_instances = query.NETQ_INST in self.requested_data + do_groups = do_instances or (query.NETQ_GROUP in self.requested_data) + do_stats = query.NETQ_STATS in self.requested_data + cluster = lu.cfg.GetClusterInfo() + + network_to_groups = None + network_to_instances = None + stats = None + + # For NETQ_GROUP, we need to map network->[groups] + if do_groups: + all_groups = lu.cfg.GetAllNodeGroupsInfo() + network_to_groups = dict((uuid, []) for uuid in self.wanted) + default_nicpp = cluster.nicparams[constants.PP_DEFAULT] + + if do_instances: + all_instances = lu.cfg.GetAllInstancesInfo() + all_nodes = lu.cfg.GetAllNodesInfo() + network_to_instances = dict((uuid, []) for uuid in self.wanted) + + + for group in all_groups.values(): + if do_instances: + group_nodes = [node.name for node in all_nodes.values() if + node.group == group.uuid] + group_instances = [instance for instance in all_instances.values() + if instance.primary_node in group_nodes] + + for net_uuid in group.networks.keys(): + if net_uuid in network_to_groups: + netparams = group.networks[net_uuid] + mode = netparams[constants.NIC_MODE] + link = netparams[constants.NIC_LINK] + info = group.name + '(' + mode + ', ' + link + ')' + network_to_groups[net_uuid].append(info) + + if do_instances: + for instance in group_instances: + for nic in instance.nics: + if nic.network == self._all_networks[net_uuid].name: + network_to_instances[net_uuid].append(instance.name) + break + + if do_stats: + stats = {} + for uuid, net in self._all_networks.items(): + if uuid in self.wanted: + pool = network.AddressPool(net) + stats[uuid] = { + "free_count": pool.GetFreeCount(), + "reserved_count": pool.GetReservedCount(), + "map": pool.GetMap(), + "external_reservations": ", ".join(pool.GetExternalReservations()), + } + + return query.NetworkQueryData([self._all_networks[uuid] + for uuid in self.wanted], + network_to_groups, + network_to_instances, + stats) + + +class LUNetworkQuery(NoHooksLU): + """Logical unit for querying networks. + + """ + REQ_BGL = False + + def CheckArguments(self): + self.nq = _NetworkQuery(qlang.MakeSimpleFilter("name", self.op.names), + self.op.output_fields, False) + + def ExpandNames(self): + self.nq.ExpandNames(self) + + def Exec(self, feedback_fn): + return self.nq.OldStyleQuery(self) + + + +class LUNetworkConnect(LogicalUnit): + """Connect a network to a nodegroup + + """ + HPATH = "network-connect" + HTYPE = constants.HTYPE_NETWORK + REQ_BGL = False + + def ExpandNames(self): + self.network_name = self.op.network_name + self.group_name = self.op.group_name + self.network_mode = self.op.network_mode + self.network_link = self.op.network_link + + self.network_uuid = self.cfg.LookupNetwork(self.network_name) + self.network = self.cfg.GetNetwork(self.network_uuid) + self.group_uuid = self.cfg.LookupNodeGroup(self.group_name) + self.group = self.cfg.GetNodeGroup(self.group_uuid) + + self.needed_locks = { + locking.LEVEL_INSTANCE: [], + locking.LEVEL_NODEGROUP: [self.group_uuid], + } + self.share_locks[locking.LEVEL_INSTANCE] = 1 + + def DeclareLocks(self, level): + if level == locking.LEVEL_INSTANCE: + assert not self.needed_locks[locking.LEVEL_INSTANCE] + + # Lock instances optimistically, needs verification once group lock has + # been acquired + self.needed_locks[locking.LEVEL_INSTANCE] = \ + self.cfg.GetNodeGroupInstances(self.group_uuid) + + def BuildHooksEnv(self): + ret = dict() + ret["GROUP_NAME"] = self.group_name + ret["GROUP_NETWORK_NAME"] = self.network_name + ret["GROUP_NETWORK_MODE"] = self.network_mode + ret["GROUP_NETWORK_LINK"] = self.network_link + return ret + + def BuildHooksNodes(self): + nodes = self.cfg.GetNodeGroup(self.group_uuid).members + return (nodes, nodes) + + + def CheckPrereq(self): + l = lambda value: ", ".join("%s: %s/%s" % (i[0], i[1], i[2]) + for i in value) + + if self.network is None: + raise errors.OpPrereqError("Network %s does not exist" % + self.network_name, errors.ECODE_INVAL) + + self.netparams = dict() + self.netparams[constants.NIC_MODE] = self.network_mode + self.netparams[constants.NIC_LINK] = self.network_link + objects.NIC.CheckParameterSyntax(self.netparams) + + #if self.network_mode == constants.NIC_MODE_BRIDGED: + # _CheckNodeGroupBridgesExist(self, self.network_link, self.group_uuid) + self.connected = False + if self.network_uuid in self.group.networks: + self.LogWarning("Network '%s' is already mapped to group '%s'" % + (self.network_name, self.group.name)) + self.connected = True + return + + pool = network.AddressPool(self.network) + if self.op.conflicts_check: + groupinstances = [] + for n in self.cfg.GetNodeGroupInstances(self.group_uuid): + groupinstances.append(self.cfg.GetInstanceInfo(n)) + instances = [(instance.name, idx, nic.ip) + for instance in groupinstances + for idx, nic in enumerate(instance.nics) + if (not nic.network and pool._Contains(nic.ip))] + if instances: + self.LogWarning("Following occurences use IPs from network %s" + " that is about to connect to nodegroup %s: %s" % + (self.network_name, self.group.name, + l(instances))) + raise errors.OpPrereqError("Conflicting IPs found." + " Please remove/modify" + " corresponding NICs", + errors.ECODE_INVAL) + + def Exec(self, feedback_fn): + if self.connected: + return + + self.group.networks[self.network_uuid] = self.netparams + self.cfg.Update(self.group, feedback_fn) + + +class LUNetworkDisconnect(LogicalUnit): + """Disconnect a network to a nodegroup + + """ + HPATH = "network-disconnect" + HTYPE = constants.HTYPE_NETWORK + REQ_BGL = False + + def ExpandNames(self): + self.network_name = self.op.network_name + self.group_name = self.op.group_name + + self.network_uuid = self.cfg.LookupNetwork(self.network_name) + self.network = self.cfg.GetNetwork(self.network_uuid) + self.group_uuid = self.cfg.LookupNodeGroup(self.group_name) + self.group = self.cfg.GetNodeGroup(self.group_uuid) + + self.needed_locks = { + locking.LEVEL_INSTANCE: [], + locking.LEVEL_NODEGROUP: [self.group_uuid], + } + self.share_locks[locking.LEVEL_INSTANCE] = 1 + + def DeclareLocks(self, level): + if level == locking.LEVEL_INSTANCE: + assert not self.needed_locks[locking.LEVEL_INSTANCE] + + # Lock instances optimistically, needs verification once group lock has + # been acquired + self.needed_locks[locking.LEVEL_INSTANCE] = \ + self.cfg.GetNodeGroupInstances(self.group_uuid) + + def BuildHooksEnv(self): + ret = dict() + ret["GROUP_NAME"] = self.group_name + ret["GROUP_NETWORK_NAME"] = self.network_name + return ret + + def BuildHooksNodes(self): + nodes = self.cfg.GetNodeGroup(self.group_uuid).members + return (nodes, nodes) + + + def CheckPrereq(self): + l = lambda value: ", ".join("%s: %s/%s" % (i[0], i[1], i[2]) + for i in value) + + self.connected = True + if self.network_uuid not in self.group.networks: + self.LogWarning("Network '%s' is" + " not mapped to group '%s'" % + (self.network_name, self.group.name)) + self.connected = False + return + + if self.op.conflicts_check: + groupinstances = [] + for n in self.cfg.GetNodeGroupInstances(self.group_uuid): + groupinstances.append(self.cfg.GetInstanceInfo(n)) + instances = [(instance.name, idx, nic.ip) + for instance in groupinstances + for idx, nic in enumerate(instance.nics) + if nic.network == self.network_name] + if instances: + self.LogWarning("Following occurences use IPs from network %s" + " that is about to disconnected from the nodegroup" + " %s: %s" % + (self.network_name, self.group.name, + l(instances))) + raise errors.OpPrereqError("Conflicting IPs." + " Please remove/modify" + " corresponding NICS", + errors.ECODE_INVAL) + + def Exec(self, feedback_fn): + if not self.connected: + return + + del self.group.networks[self.network_uuid] + self.cfg.Update(self.group, feedback_fn) + #: Query type implementations _QUERY_IMPL = { @@ -15323,6 +16003,7 @@ _QUERY_IMPL = { constants.QR_INSTANCE: _InstanceQuery, constants.QR_NODE: _NodeQuery, constants.QR_GROUP: _GroupQuery, + constants.QR_NETWORK: _NetworkQuery, constants.QR_OS: _OsQuery, constants.QR_EXPORT: _ExportQuery, } @@ -15341,3 +16022,20 @@ def _GetQueryImplementation(name): except KeyError: raise errors.OpPrereqError("Unknown query resource '%s'" % name, errors.ECODE_INVAL) + +def _CheckForConflictingIp(lu, ip, node): + """In case of conflicting ip raise error. + + @type ip: string + @param ip: ip address + @type node: string + @param node: node name + + """ + (conf_net, conf_netparams) = lu.cfg.CheckIPInNodeGroup(ip, node) + if conf_net is not None: + raise errors.OpPrereqError("Conflicting IP found:" + " %s <> %s." % (ip, conf_net), + errors.ECODE_INVAL) + + return (None, None)