| # |
| # |
| |
| # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, but |
| # WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| # General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| # 02110-1301, USA. |
| |
| |
| """Logical units dealing with networks.""" |
| |
| from ganeti import constants |
| from ganeti import errors |
| from ganeti import locking |
| from ganeti import network |
| from ganeti import objects |
| from ganeti import qlang |
| from ganeti import query |
| from ganeti import utils |
| from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase |
| from ganeti.cmdlib.common import ShareAll, CheckNodeGroupInstances |
| |
| |
| def _BuildNetworkHookEnv(name, subnet, gateway, network6, gateway6, |
| mac_prefix, tags): |
| """Builds network related env variables for hooks |
| |
| This builds the hook environment from individual variables. |
| |
| @type name: string |
| @param name: the name of the network |
| @type subnet: string |
| @param subnet: the ipv4 subnet |
| @type gateway: string |
| @param gateway: the ipv4 gateway |
| @type network6: string |
| @param network6: the ipv6 subnet |
| @type gateway6: string |
| @param gateway6: the ipv6 gateway |
| @type mac_prefix: string |
| @param mac_prefix: the mac_prefix |
| @type tags: list |
| @param tags: the tags of the network |
| |
| """ |
| env = {} |
| if name: |
| env["NETWORK_NAME"] = name |
| if subnet: |
| env["NETWORK_SUBNET"] = subnet |
| if gateway: |
| env["NETWORK_GATEWAY"] = gateway |
| if network6: |
| env["NETWORK_SUBNET6"] = network6 |
| if gateway6: |
| env["NETWORK_GATEWAY6"] = gateway6 |
| if mac_prefix: |
| env["NETWORK_MAC_PREFIX"] = mac_prefix |
| if tags: |
| env["NETWORK_TAGS"] = " ".join(tags) |
| |
| return env |
| |
| |
| 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 CheckArguments(self): |
| if self.op.mac_prefix: |
| self.op.mac_prefix = \ |
| utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix) |
| |
| def ExpandNames(self): |
| self.network_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId()) |
| |
| if self.op.conflicts_check: |
| self.share_locks[locking.LEVEL_NODE] = 1 |
| self.share_locks[locking.LEVEL_NODE_ALLOC] = 1 |
| self.needed_locks = { |
| locking.LEVEL_NODE: locking.ALL_SET, |
| locking.LEVEL_NODE_ALLOC: locking.ALL_SET, |
| } |
| else: |
| self.needed_locks = {} |
| |
| self.add_locks[locking.LEVEL_NETWORK] = self.network_uuid |
| |
| def CheckPrereq(self): |
| if self.op.network is None: |
| raise errors.OpPrereqError("Network must be given", |
| errors.ECODE_INVAL) |
| |
| try: |
| existing_uuid = self.cfg.LookupNetwork(self.op.network_name) |
| except errors.OpPrereqError: |
| pass |
| else: |
| raise errors.OpPrereqError("Desired network name '%s' already exists as a" |
| " network (UUID: %s)" % |
| (self.op.network_name, existing_uuid), |
| errors.ECODE_EXISTS) |
| |
| # Check tag validity |
| for tag in self.op.tags: |
| objects.TaggableObject.ValidateTag(tag) |
| |
| def BuildHooksEnv(self): |
| """Build hooks env. |
| |
| """ |
| args = { |
| "name": self.op.network_name, |
| "subnet": self.op.network, |
| "gateway": self.op.gateway, |
| "network6": self.op.network6, |
| "gateway6": self.op.gateway6, |
| "mac_prefix": self.op.mac_prefix, |
| "tags": self.op.tags, |
| } |
| return _BuildNetworkHookEnv(**args) # pylint: disable=W0142 |
| |
| 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, |
| uuid=self.network_uuid) |
| # Initialize the associated address pool |
| try: |
| pool = network.AddressPool.InitializeNetwork(nobj) |
| except errors.AddressPoolError, err: |
| raise errors.OpExecError("Cannot create IP address pool for network" |
| " '%s': %s" % (self.op.network_name, err)) |
| |
| # 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. |
| if self.op.conflicts_check: |
| for node in self.cfg.GetAllNodesInfo().values(): |
| for ip in [node.primary_ip, node.secondary_ip]: |
| try: |
| if pool.Contains(ip): |
| pool.Reserve(ip) |
| self.LogInfo("Reserved IP address of node '%s' (%s)", |
| node.name, ip) |
| except errors.AddressPoolError, err: |
| self.LogWarning("Cannot reserve IP address '%s' of node '%s': %s", |
| ip, node.name, err) |
| |
| master_ip = self.cfg.GetClusterInfo().master_ip |
| try: |
| if pool.Contains(master_ip): |
| pool.Reserve(master_ip) |
| self.LogInfo("Reserved cluster master IP address (%s)", master_ip) |
| except errors.AddressPoolError, err: |
| self.LogWarning("Cannot reserve cluster master IP address (%s): %s", |
| master_ip, err) |
| |
| if self.op.add_reserved_ips: |
| for ip in self.op.add_reserved_ips: |
| try: |
| pool.Reserve(ip, external=True) |
| except errors.AddressPoolError, err: |
| raise errors.OpExecError("Cannot reserve IP address '%s': %s" % |
| (ip, err)) |
| |
| if self.op.tags: |
| for tag in self.op.tags: |
| nobj.AddTag(tag) |
| |
| 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.share_locks[locking.LEVEL_NODEGROUP] = 1 |
| self.needed_locks = { |
| locking.LEVEL_NETWORK: [self.network_uuid], |
| locking.LEVEL_NODEGROUP: locking.ALL_SET, |
| } |
| |
| 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. |
| |
| """ |
| # Verify that the network is not conncted. |
| node_groups = [group.name |
| for group in self.cfg.GetAllNodeGroupsInfo().values() |
| if self.network_uuid in group.networks] |
| |
| if node_groups: |
| self.LogWarning("Network '%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.needed_locks = { |
| locking.LEVEL_NETWORK: [self.network_uuid], |
| } |
| |
| def CheckPrereq(self): |
| """Check prerequisites. |
| |
| """ |
| self.network = self.cfg.GetNetwork(self.network_uuid) |
| self.gateway = self.network.gateway |
| self.mac_prefix = self.network.mac_prefix |
| self.network6 = self.network.network6 |
| self.gateway6 = self.network.gateway6 |
| self.tags = self.network.tags |
| |
| 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("Gateway IP address '%s' is already" |
| " reserved" % self.gateway, |
| errors.ECODE_STATE) |
| |
| if self.op.mac_prefix: |
| if self.op.mac_prefix == constants.VALUE_NONE: |
| self.mac_prefix = None |
| else: |
| self.mac_prefix = \ |
| utils.NormalizeAndValidateThreeOctetMacPrefix(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. |
| |
| """ |
| args = { |
| "name": self.op.network_name, |
| "subnet": self.network.network, |
| "gateway": self.gateway, |
| "network6": self.network6, |
| "gateway6": self.gateway6, |
| "mac_prefix": self.mac_prefix, |
| "tags": self.tags, |
| } |
| return _BuildNetworkHookEnv(**args) # pylint: disable=W0142 |
| |
| 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 address %s is already reserved", ip) |
| else: |
| self.pool.Reserve(ip, external=True) |
| except errors.AddressPoolError, err: |
| self.LogWarning("Cannot reserve IP address %s: %s", ip, err) |
| |
| 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 address %s is already unreserved", ip) |
| else: |
| self.pool.Release(ip, external=True) |
| except errors.AddressPoolError, err: |
| self.LogWarning("Cannot release IP address %s: %s", ip, err) |
| |
| 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 |
| |
| self.pool.Validate() |
| |
| self.cfg.Update(self.network, feedback_fn) |
| |
| |
| class NetworkQuery(QueryBase): |
| FIELDS = query.NETWORK_FIELDS |
| |
| def ExpandNames(self, lu): |
| lu.needed_locks = {} |
| lu.share_locks = ShareAll() |
| |
| self.do_locking = self.use_locking |
| |
| all_networks = lu.cfg.GetAllNetworksInfo() |
| name_to_uuid = dict((n.name, n.uuid) for n in all_networks.values()) |
| |
| if self.names: |
| missing = [] |
| self.wanted = [] |
| |
| for name in self.names: |
| if 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) |
| else: |
| self.wanted = locking.ALL_SET |
| |
| if self.do_locking: |
| lu.needed_locks[locking.LEVEL_NETWORK] = self.wanted |
| if query.NETQ_INST in self.requested_data: |
| lu.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET |
| if query.NETQ_GROUP in self.requested_data: |
| lu.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET |
| |
| def DeclareLocks(self, lu, level): |
| pass |
| |
| def _GetQueryData(self, lu): |
| """Computes the list of networks and their attributes. |
| |
| """ |
| all_networks = lu.cfg.GetAllNetworksInfo() |
| |
| network_uuids = self._GetNames(lu, all_networks.keys(), |
| locking.LEVEL_NETWORK) |
| |
| do_instances = query.NETQ_INST in self.requested_data |
| do_groups = query.NETQ_GROUP in self.requested_data |
| |
| network_to_instances = None |
| network_to_groups = 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 network_uuids) |
| for _, group in all_groups.iteritems(): |
| for net_uuid in network_uuids: |
| netparams = group.networks.get(net_uuid, None) |
| if netparams: |
| info = (group.name, netparams[constants.NIC_MODE], |
| netparams[constants.NIC_LINK]) |
| |
| network_to_groups[net_uuid].append(info) |
| |
| if do_instances: |
| all_instances = lu.cfg.GetAllInstancesInfo() |
| network_to_instances = dict((uuid, []) for uuid in network_uuids) |
| for instance in all_instances.values(): |
| for nic in instance.nics: |
| if nic.network in network_uuids: |
| network_to_instances[nic.network].append(instance.uuid) |
| break |
| |
| if query.NETQ_STATS in self.requested_data: |
| stats = \ |
| dict((uuid, |
| self._GetStats(network.AddressPool(all_networks[uuid]))) |
| for uuid in network_uuids) |
| else: |
| stats = None |
| |
| return query.NetworkQueryData([all_networks[uuid] |
| for uuid in network_uuids], |
| network_to_groups, |
| network_to_instances, |
| stats) |
| |
| @staticmethod |
| def _GetStats(pool): |
| """Returns statistics for a network address pool. |
| |
| """ |
| return { |
| "free_count": pool.GetFreeCount(), |
| "reserved_count": pool.GetReservedCount(), |
| "map": pool.GetMap(), |
| "external_reservations": |
| utils.CommaJoin(pool.GetExternalReservations()), |
| } |
| |
| |
| 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, self.op.use_locking) |
| |
| def ExpandNames(self): |
| self.nq.ExpandNames(self) |
| |
| def Exec(self, feedback_fn): |
| return self.nq.OldStyleQuery(self) |
| |
| |
| def _FmtNetworkConflict(details): |
| """Utility for L{_NetworkConflictCheck}. |
| |
| """ |
| return utils.CommaJoin("nic%s/%s" % (idx, ipaddr) |
| for (idx, ipaddr) in details) |
| |
| |
| def _NetworkConflictCheck(lu, check_fn, action, instances): |
| """Checks for network interface conflicts with a network. |
| |
| @type lu: L{LogicalUnit} |
| @type check_fn: callable receiving one parameter (L{objects.NIC}) and |
| returning boolean |
| @param check_fn: Function checking for conflict |
| @type action: string |
| @param action: Part of error message (see code) |
| @param instances: the instances to check |
| @type instances: list of instance objects |
| @raise errors.OpPrereqError: If conflicting IP addresses are found. |
| |
| """ |
| conflicts = [] |
| |
| for instance in instances: |
| instconflicts = [(idx, nic.ip) |
| for (idx, nic) in enumerate(instance.nics) |
| if check_fn(nic)] |
| |
| if instconflicts: |
| conflicts.append((instance.name, instconflicts)) |
| |
| if conflicts: |
| lu.LogWarning("IP addresses from network '%s', which is about to %s" |
| " node group '%s', are in use: %s" % |
| (lu.network_name, action, lu.group.name, |
| utils.CommaJoin(("%s: %s" % |
| (name, _FmtNetworkConflict(details))) |
| for (name, details) in conflicts))) |
| |
| raise errors.OpPrereqError("Conflicting IP addresses found; " |
| " remove/modify the corresponding network" |
| " interfaces", errors.ECODE_STATE) |
| |
| |
| 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.group_uuid = self.cfg.LookupNodeGroup(self.group_name) |
| |
| self.needed_locks = { |
| locking.LEVEL_INSTANCE: [], |
| locking.LEVEL_NODEGROUP: [self.group_uuid], |
| } |
| self.share_locks[locking.LEVEL_INSTANCE] = 1 |
| |
| if self.op.conflicts_check: |
| self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid] |
| self.share_locks[locking.LEVEL_NETWORK] = 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 |
| if self.op.conflicts_check: |
| self.needed_locks[locking.LEVEL_INSTANCE] = \ |
| self.cfg.GetInstanceNames( |
| self.cfg.GetNodeGroupInstances(self.group_uuid)) |
| |
| def BuildHooksEnv(self): |
| ret = { |
| "GROUP_NAME": self.group_name, |
| "GROUP_NETWORK_MODE": self.network_mode, |
| "GROUP_NETWORK_LINK": self.network_link, |
| } |
| return ret |
| |
| def BuildHooksNodes(self): |
| node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members |
| return (node_uuids, node_uuids) |
| |
| def CheckPrereq(self): |
| owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) |
| |
| assert self.group_uuid in owned_groups |
| |
| # Check if locked instances are still correct |
| owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) |
| if self.op.conflicts_check: |
| CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names) |
| |
| self.netparams = { |
| constants.NIC_MODE: self.network_mode, |
| constants.NIC_LINK: self.network_link, |
| } |
| objects.NIC.CheckParameterSyntax(self.netparams) |
| |
| self.group = self.cfg.GetNodeGroup(self.group_uuid) |
| #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 |
| |
| # check only if not already connected |
| elif self.op.conflicts_check: |
| pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid)) |
| |
| _NetworkConflictCheck( |
| self, lambda nic: pool.Contains(nic.ip), "connect to", |
| [instance_info for (_, instance_info) in |
| self.cfg.GetMultiInstanceInfoByName(owned_instance_names)]) |
| |
| def Exec(self, feedback_fn): |
| # Connect the network and update the group only if not already connected |
| if not self.connected: |
| 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.group_uuid = self.cfg.LookupNodeGroup(self.group_name) |
| |
| 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.GetInstanceNames( |
| self.cfg.GetNodeGroupInstances(self.group_uuid)) |
| |
| def BuildHooksEnv(self): |
| ret = { |
| "GROUP_NAME": self.group_name, |
| } |
| return ret |
| |
| def BuildHooksNodes(self): |
| nodes = self.cfg.GetNodeGroup(self.group_uuid).members |
| return (nodes, nodes) |
| |
| def CheckPrereq(self): |
| owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) |
| |
| assert self.group_uuid in owned_groups |
| |
| # Check if locked instances are still correct |
| owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) |
| CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances) |
| |
| self.group = self.cfg.GetNodeGroup(self.group_uuid) |
| 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 |
| |
| # We need this check only if network is not already connected |
| else: |
| _NetworkConflictCheck( |
| self, lambda nic: nic.network == self.network_uuid, "disconnect from", |
| [instance_info for (_, instance_info) in |
| self.cfg.GetMultiInstanceInfoByName(owned_instances)]) |
| |
| def Exec(self, feedback_fn): |
| # Disconnect the network and update the group only if network is connected |
| if self.connected: |
| del self.group.networks[self.network_uuid] |
| self.cfg.Update(self.group, feedback_fn) |