Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / network.py @ 02aa7df1

History | View | Annotate | Download (21.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Logical units dealing with networks."""
23

    
24
from ganeti import constants
25
from ganeti import errors
26
from ganeti import locking
27
from ganeti import network
28
from ganeti import objects
29
from ganeti import qlang
30
from ganeti import query
31
from ganeti import utils
32
from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase
33
from ganeti.cmdlib.common import ShareAll, CheckNodeGroupInstances
34

    
35

    
36
def _BuildNetworkHookEnv(name, subnet, gateway, network6, gateway6,
37
                         mac_prefix, tags):
38
  """Builds network related env variables for hooks
39

40
  This builds the hook environment from individual variables.
41

42
  @type name: string
43
  @param name: the name of the network
44
  @type subnet: string
45
  @param subnet: the ipv4 subnet
46
  @type gateway: string
47
  @param gateway: the ipv4 gateway
48
  @type network6: string
49
  @param network6: the ipv6 subnet
50
  @type gateway6: string
51
  @param gateway6: the ipv6 gateway
52
  @type mac_prefix: string
53
  @param mac_prefix: the mac_prefix
54
  @type tags: list
55
  @param tags: the tags of the network
56

57
  """
58
  env = {}
59
  if name:
60
    env["NETWORK_NAME"] = name
61
  if subnet:
62
    env["NETWORK_SUBNET"] = subnet
63
  if gateway:
64
    env["NETWORK_GATEWAY"] = gateway
65
  if network6:
66
    env["NETWORK_SUBNET6"] = network6
67
  if gateway6:
68
    env["NETWORK_GATEWAY6"] = gateway6
69
  if mac_prefix:
70
    env["NETWORK_MAC_PREFIX"] = mac_prefix
71
  if tags:
72
    env["NETWORK_TAGS"] = " ".join(tags)
73

    
74
  return env
75

    
76

    
77
class LUNetworkAdd(LogicalUnit):
78
  """Logical unit for creating networks.
79

80
  """
81
  HPATH = "network-add"
82
  HTYPE = constants.HTYPE_NETWORK
83
  REQ_BGL = False
84

    
85
  def BuildHooksNodes(self):
86
    """Build hooks nodes.
87

88
    """
89
    mn = self.cfg.GetMasterNode()
90
    return ([mn], [mn])
91

    
92
  def CheckArguments(self):
93
    if self.op.mac_prefix:
94
      self.op.mac_prefix = \
95
        utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
96

    
97
  def ExpandNames(self):
98
    self.network_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
99

    
100
    if self.op.conflicts_check:
101
      self.share_locks[locking.LEVEL_NODE] = 1
102
      self.share_locks[locking.LEVEL_NODE_ALLOC] = 1
103
      self.needed_locks = {
104
        locking.LEVEL_NODE: locking.ALL_SET,
105
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
106
        }
107
    else:
108
      self.needed_locks = {}
109

    
110
    self.add_locks[locking.LEVEL_NETWORK] = self.network_uuid
111

    
112
  def CheckPrereq(self):
113
    try:
114
      existing_uuid = self.cfg.LookupNetwork(self.op.network_name)
115
    except errors.OpPrereqError:
116
      pass
117
    else:
118
      raise errors.OpPrereqError("Desired network name '%s' already exists as a"
119
                                 " network (UUID: %s)" %
120
                                 (self.op.network_name, existing_uuid),
121
                                 errors.ECODE_EXISTS)
122

    
123
    # Check tag validity
124
    for tag in self.op.tags:
125
      objects.TaggableObject.ValidateTag(tag)
126

    
127
    self.nobj = objects.Network(name=self.op.network_name,
128
                                network=self.op.network,
129
                                gateway=self.op.gateway,
130
                                network6=self.op.network6,
131
                                gateway6=self.op.gateway6,
132
                                mac_prefix=self.op.mac_prefix,
133
                                uuid=self.network_uuid)
134

    
135
    # Initialize the associated address pool
136
    # This raises the appropriate OpPrereqError
137
    self.pool = network.Network(self.nobj)
138

    
139
  def BuildHooksEnv(self):
140
    """Build hooks env.
141

142
    """
143
    args = {
144
      "name": self.op.network_name,
145
      "subnet": self.op.network,
146
      "gateway": self.op.gateway,
147
      "network6": self.op.network6,
148
      "gateway6": self.op.gateway6,
149
      "mac_prefix": self.op.mac_prefix,
150
      "tags": self.op.tags,
151
      }
152
    return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
153

    
154
  def Exec(self, feedback_fn):
155
    """Add the ip pool to the cluster.
156

157
    """
158
    # Check if we need to reserve the nodes and the cluster master IP
159
    # These may not be allocated to any instances in routed mode, as
160
    # they wouldn't function anyway.
161
    if self.op.conflicts_check:
162
      for node in self.cfg.GetAllNodesInfo().values():
163
        for ip in [node.primary_ip, node.secondary_ip]:
164
          if self.pool.Contains(ip):
165
            self.pool.Reserve(ip, True)
166
            self.LogInfo("Reserved IP address of node '%s' (%s)", node.name, ip)
167

    
168
      master_ip = self.cfg.GetClusterInfo().master_ip
169
      if self.pool.Contains(master_ip):
170
        self.pool.Reserve(master_ip, True)
171
        self.LogInfo("Reserved cluster master IP address (%s)", master_ip)
172

    
173
    if self.op.add_reserved_ips:
174
      for ip in self.op.add_reserved_ips:
175
        try:
176
          self.pool.Reserve(ip, external=True)
177
        except errors.AddressPoolError, err:
178
          raise errors.OpExecError("Cannot reserve IP address '%s': %s" %
179
                                   (ip, err))
180

    
181
    if self.op.tags:
182
      for tag in self.op.tags:
183
        self.nobj.AddTag(tag)
184

    
185
    self.cfg.AddNetwork(self.nobj, self.proc.GetECId(), check_uuid=False)
186
    del self.remove_locks[locking.LEVEL_NETWORK]
187

    
188

    
189
class LUNetworkRemove(LogicalUnit):
190
  HPATH = "network-remove"
191
  HTYPE = constants.HTYPE_NETWORK
192
  REQ_BGL = False
193

    
194
  def ExpandNames(self):
195
    self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
196

    
197
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
198
    self.needed_locks = {
199
      locking.LEVEL_NETWORK: [self.network_uuid],
200
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
201
      }
202

    
203
  def CheckPrereq(self):
204
    """Check prerequisites.
205

206
    This checks that the given network name exists as a network, that is
207
    empty (i.e., contains no nodes), and that is not the last group of the
208
    cluster.
209

210
    """
211
    # Verify that the network is not conncted.
212
    node_groups = [group.name
213
                   for group in self.cfg.GetAllNodeGroupsInfo().values()
214
                   if self.network_uuid in group.networks]
215

    
216
    if node_groups:
217
      self.LogWarning("Network '%s' is connected to the following"
218
                      " node groups: %s" %
219
                      (self.op.network_name,
220
                       utils.CommaJoin(utils.NiceSort(node_groups))))
221
      raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
222

    
223
  def BuildHooksEnv(self):
224
    """Build hooks env.
225

226
    """
227
    return {
228
      "NETWORK_NAME": self.op.network_name,
229
      }
230

    
231
  def BuildHooksNodes(self):
232
    """Build hooks nodes.
233

234
    """
235
    mn = self.cfg.GetMasterNode()
236
    return ([mn], [mn])
237

    
238
  def Exec(self, feedback_fn):
239
    """Remove the network.
240

241
    """
242
    try:
243
      self.cfg.RemoveNetwork(self.network_uuid)
244
    except errors.ConfigurationError:
245
      raise errors.OpExecError("Network '%s' with UUID %s disappeared" %
246
                               (self.op.network_name, self.network_uuid))
247

    
248

    
249
class LUNetworkSetParams(LogicalUnit):
250
  """Modifies the parameters of a network.
251

252
  """
253
  HPATH = "network-modify"
254
  HTYPE = constants.HTYPE_NETWORK
255
  REQ_BGL = False
256

    
257
  def CheckArguments(self):
258
    if (self.op.gateway and
259
        (self.op.add_reserved_ips or self.op.remove_reserved_ips)):
260
      raise errors.OpPrereqError("Cannot modify gateway and reserved ips"
261
                                 " at once", errors.ECODE_INVAL)
262

    
263
  def ExpandNames(self):
264
    self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
265

    
266
    self.needed_locks = {
267
      locking.LEVEL_NETWORK: [self.network_uuid],
268
      }
269

    
270
  def CheckPrereq(self):
271
    """Check prerequisites.
272

273
    """
274
    self.nobj = self.cfg.GetNetwork(self.network_uuid)
275
    self.gateway = self.network.gateway
276
    self.mac_prefix = self.network.mac_prefix
277
    self.network6 = self.network.network6
278
    self.gateway6 = self.network.gateway6
279
    self.tags = self.network.tags
280

    
281
    self.pool = network.Network(self.nobj)
282

    
283
    if self.op.gateway:
284
      if self.op.gateway == constants.VALUE_NONE:
285
        self.gateway = None
286
      else:
287
        self.gateway = self.op.gateway
288

    
289
    network.Network.Check(self.gateway, self.network)
290

    
291
    if self.op.mac_prefix:
292
      if self.op.mac_prefix == constants.VALUE_NONE:
293
        self.mac_prefix = None
294
      else:
295
        self.mac_prefix = \
296
          utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
297

    
298
    if self.op.gateway6:
299
      if self.op.gateway6 == constants.VALUE_NONE:
300
        self.gateway6 = None
301
      else:
302
        self.gateway6 = self.op.gateway6
303

    
304
    if self.op.network6:
305
      if self.op.network6 == constants.VALUE_NONE:
306
        self.network6 = None
307
      else:
308
        self.network6 = self.op.network6
309

    
310
    network.Network.Check(self.gateway, self.network)
311

    
312
  def BuildHooksEnv(self):
313
    """Build hooks env.
314

315
    """
316
    args = {
317
      "name": self.op.network_name,
318
      "subnet": self.network.network,
319
      "gateway": self.gateway,
320
      "network6": self.network6,
321
      "gateway6": self.gateway6,
322
      "mac_prefix": self.mac_prefix,
323
      "tags": self.tags,
324
      }
325
    return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
326

    
327
  def BuildHooksNodes(self):
328
    """Build hooks nodes.
329

330
    """
331
    mn = self.cfg.GetMasterNode()
332
    return ([mn], [mn])
333

    
334
  def Exec(self, feedback_fn):
335
    """Modifies the network.
336

337
    """
338
    #TODO: reserve/release via temporary reservation manager
339
    #      extend cfg.ReserveIp/ReleaseIp with the external flag
340
    ec_id = self.proc.GetECId()
341
    if self.op.gateway:
342
      if self.gateway == self.network.gateway:
343
        self.LogWarning("Gateway is already %s", self.gateway)
344
      else:
345
        if self.gateway:
346
          self.cfg.ReserveIp(self.network_uuid, self.gateway, True, ec_id)
347
        if self.network.gateway:
348
          self.cfg.ReleaseIp(self.network_uuid,
349
                             self.network.gateway, True, ec_id)
350
        self.network.gateway = self.gateway
351

    
352
    if self.op.add_reserved_ips:
353
      for ip in self.op.add_reserved_ips:
354
        self.cfg.ReserveIp(self.network_uuid, ip, True, ec_id)
355

    
356
    if self.op.remove_reserved_ips:
357
      for ip in self.op.remove_reserved_ips:
358
        if ip == self.network.gateway:
359
          self.LogWarning("Cannot unreserve Gateway's IP")
360
          continue
361
        self.cfg.ReleaseIp(self.network_uuid, ip, True, ec_id)
362

    
363
    if self.op.mac_prefix:
364
      self.network.mac_prefix = self.mac_prefix
365

    
366
    if self.op.network6:
367
      self.network.network6 = self.network6
368

    
369
    if self.op.gateway6:
370
      self.network.gateway6 = self.gateway6
371

    
372
    self.pool.Validate()
373

    
374
    self.cfg.Update(self.network, feedback_fn)
375

    
376

    
377
class NetworkQuery(QueryBase):
378
  FIELDS = query.NETWORK_FIELDS
379

    
380
  def ExpandNames(self, lu):
381
    lu.needed_locks = {}
382
    lu.share_locks = ShareAll()
383

    
384
    self.do_locking = self.use_locking
385

    
386
    all_networks = lu.cfg.GetAllNetworksInfo()
387
    name_to_uuid = dict((n.name, n.uuid) for n in all_networks.values())
388

    
389
    if self.names:
390
      missing = []
391
      self.wanted = []
392

    
393
      for name in self.names:
394
        if name in name_to_uuid:
395
          self.wanted.append(name_to_uuid[name])
396
        else:
397
          missing.append(name)
398

    
399
      if missing:
400
        raise errors.OpPrereqError("Some networks do not exist: %s" % missing,
401
                                   errors.ECODE_NOENT)
402
    else:
403
      self.wanted = locking.ALL_SET
404

    
405
    if self.do_locking:
406
      lu.needed_locks[locking.LEVEL_NETWORK] = self.wanted
407
      if query.NETQ_INST in self.requested_data:
408
        lu.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
409
      if query.NETQ_GROUP in self.requested_data:
410
        lu.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET
411

    
412
  def DeclareLocks(self, lu, level):
413
    pass
414

    
415
  def _GetQueryData(self, lu):
416
    """Computes the list of networks and their attributes.
417

418
    """
419
    all_networks = lu.cfg.GetAllNetworksInfo()
420

    
421
    network_uuids = self._GetNames(lu, all_networks.keys(),
422
                                   locking.LEVEL_NETWORK)
423

    
424
    do_instances = query.NETQ_INST in self.requested_data
425
    do_groups = query.NETQ_GROUP in self.requested_data
426

    
427
    network_to_instances = None
428
    network_to_groups = None
429

    
430
    # For NETQ_GROUP, we need to map network->[groups]
431
    if do_groups:
432
      all_groups = lu.cfg.GetAllNodeGroupsInfo()
433
      network_to_groups = dict((uuid, []) for uuid in network_uuids)
434
      for _, group in all_groups.iteritems():
435
        for net_uuid in network_uuids:
436
          netparams = group.networks.get(net_uuid, None)
437
          if netparams:
438
            info = (group.name, netparams[constants.NIC_MODE],
439
                    netparams[constants.NIC_LINK])
440

    
441
            network_to_groups[net_uuid].append(info)
442

    
443
    if do_instances:
444
      all_instances = lu.cfg.GetAllInstancesInfo()
445
      network_to_instances = dict((uuid, []) for uuid in network_uuids)
446
      for instance in all_instances.values():
447
        for nic in instance.nics:
448
          if nic.network in network_uuids:
449
            network_to_instances[nic.network].append(instance.name)
450
            break
451

    
452
    if query.NETQ_STATS in self.requested_data:
453
      stats = \
454
        dict((uuid, network.Network(all_networks[uuid]).GetStats())
455
             for uuid in network_uuids)
456
    else:
457
      stats = None
458

    
459
    return query.NetworkQueryData([all_networks[uuid]
460
                                   for uuid in network_uuids],
461
                                   network_to_groups,
462
                                   network_to_instances,
463
                                   stats)
464

    
465
  @staticmethod
466
  def _GetStats(pool):
467
    """Returns statistics for a network address pool.
468

469
    """
470
    return {
471
      "free_count": pool.GetFreeCount(),
472
      "reserved_count": pool.GetReservedCount(),
473
      "map": pool.GetMap(),
474
      "external_reservations":
475
        utils.CommaJoin(pool.GetExternalReservations()),
476
      }
477

    
478

    
479
class LUNetworkQuery(NoHooksLU):
480
  """Logical unit for querying networks.
481

482
  """
483
  REQ_BGL = False
484

    
485
  def CheckArguments(self):
486
    self.nq = NetworkQuery(qlang.MakeSimpleFilter("name", self.op.names),
487
                            self.op.output_fields, self.op.use_locking)
488

    
489
  def ExpandNames(self):
490
    self.nq.ExpandNames(self)
491

    
492
  def Exec(self, feedback_fn):
493
    return self.nq.OldStyleQuery(self)
494

    
495

    
496
def _FmtNetworkConflict(details):
497
  """Utility for L{_NetworkConflictCheck}.
498

499
  """
500
  return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
501
                         for (idx, ipaddr) in details)
502

    
503

    
504
def _NetworkConflictCheck(lu, check_fn, action, instances):
505
  """Checks for network interface conflicts with a network.
506

507
  @type lu: L{LogicalUnit}
508
  @type check_fn: callable receiving one parameter (L{objects.NIC}) and
509
    returning boolean
510
  @param check_fn: Function checking for conflict
511
  @type action: string
512
  @param action: Part of error message (see code)
513
  @raise errors.OpPrereqError: If conflicting IP addresses are found.
514

515
  """
516
  conflicts = []
517

    
518
  for (_, instance) in lu.cfg.GetMultiInstanceInfo(instances):
519
    instconflicts = [(idx, nic.ip)
520
                     for (idx, nic) in enumerate(instance.nics)
521
                     if check_fn(nic)]
522

    
523
    if instconflicts:
524
      conflicts.append((instance.name, instconflicts))
525

    
526
  if conflicts:
527
    lu.LogWarning("IP addresses from network '%s', which is about to %s"
528
                  " node group '%s', are in use: %s" %
529
                  (lu.network_name, action, lu.group.name,
530
                   utils.CommaJoin(("%s: %s" %
531
                                    (name, _FmtNetworkConflict(details)))
532
                                   for (name, details) in conflicts)))
533

    
534
    raise errors.OpPrereqError("Conflicting IP addresses found; "
535
                               " remove/modify the corresponding network"
536
                               " interfaces", errors.ECODE_STATE)
537

    
538

    
539
class LUNetworkConnect(LogicalUnit):
540
  """Connect a network to a nodegroup
541

542
  """
543
  HPATH = "network-connect"
544
  HTYPE = constants.HTYPE_NETWORK
545
  REQ_BGL = False
546

    
547
  def ExpandNames(self):
548
    self.network_name = self.op.network_name
549
    self.group_name = self.op.group_name
550
    self.network_mode = self.op.network_mode
551
    self.network_link = self.op.network_link
552

    
553
    self.network_uuid = self.cfg.LookupNetwork(self.network_name)
554
    self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
555

    
556
    self.needed_locks = {
557
      locking.LEVEL_INSTANCE: [],
558
      locking.LEVEL_NODEGROUP: [self.group_uuid],
559
      }
560
    self.share_locks[locking.LEVEL_INSTANCE] = 1
561

    
562
    if self.op.conflicts_check:
563
      self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
564
      self.share_locks[locking.LEVEL_NETWORK] = 1
565

    
566
  def DeclareLocks(self, level):
567
    if level == locking.LEVEL_INSTANCE:
568
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
569

    
570
      # Lock instances optimistically, needs verification once group lock has
571
      # been acquired
572
      if self.op.conflicts_check:
573
        self.needed_locks[locking.LEVEL_INSTANCE] = \
574
            self.cfg.GetNodeGroupInstances(self.group_uuid)
575

    
576
  def BuildHooksEnv(self):
577
    ret = {
578
      "GROUP_NAME": self.group_name,
579
      "GROUP_NETWORK_MODE": self.network_mode,
580
      "GROUP_NETWORK_LINK": self.network_link,
581
      }
582
    return ret
583

    
584
  def BuildHooksNodes(self):
585
    nodes = self.cfg.GetNodeGroup(self.group_uuid).members
586
    return (nodes, nodes)
587

    
588
  def CheckPrereq(self):
589
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
590

    
591
    assert self.group_uuid in owned_groups
592

    
593
    # Check if locked instances are still correct
594
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
595
    if self.op.conflicts_check:
596
      CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
597

    
598
    self.netparams = {
599
      constants.NIC_MODE: self.network_mode,
600
      constants.NIC_LINK: self.network_link,
601
      }
602
    objects.NIC.CheckParameterSyntax(self.netparams)
603

    
604
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
605
    #if self.network_mode == constants.NIC_MODE_BRIDGED:
606
    #  _CheckNodeGroupBridgesExist(self, self.network_link, self.group_uuid)
607
    self.connected = False
608
    if self.network_uuid in self.group.networks:
609
      self.LogWarning("Network '%s' is already mapped to group '%s'" %
610
                      (self.network_name, self.group.name))
611
      self.connected = True
612

    
613
    # check only if not already connected
614
    elif self.op.conflicts_check:
615
      pool = network.Network(self.cfg.GetNetwork(self.network_uuid))
616

    
617
      _NetworkConflictCheck(self, lambda nic: pool.Contains(nic.ip),
618
                            "connect to", owned_instances)
619

    
620
  def Exec(self, feedback_fn):
621
    # Connect the network and update the group only if not already connected
622
    if not self.connected:
623
      self.group.networks[self.network_uuid] = self.netparams
624
      self.cfg.Update(self.group, feedback_fn)
625

    
626

    
627
class LUNetworkDisconnect(LogicalUnit):
628
  """Disconnect a network to a nodegroup
629

630
  """
631
  HPATH = "network-disconnect"
632
  HTYPE = constants.HTYPE_NETWORK
633
  REQ_BGL = False
634

    
635
  def ExpandNames(self):
636
    self.network_name = self.op.network_name
637
    self.group_name = self.op.group_name
638

    
639
    self.network_uuid = self.cfg.LookupNetwork(self.network_name)
640
    self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
641

    
642
    self.needed_locks = {
643
      locking.LEVEL_INSTANCE: [],
644
      locking.LEVEL_NODEGROUP: [self.group_uuid],
645
      }
646
    self.share_locks[locking.LEVEL_INSTANCE] = 1
647

    
648
  def DeclareLocks(self, level):
649
    if level == locking.LEVEL_INSTANCE:
650
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
651

    
652
      # Lock instances optimistically, needs verification once group lock has
653
      # been acquired
654
      self.needed_locks[locking.LEVEL_INSTANCE] = \
655
        self.cfg.GetNodeGroupInstances(self.group_uuid)
656

    
657
  def BuildHooksEnv(self):
658
    ret = {
659
      "GROUP_NAME": self.group_name,
660
      }
661
    return ret
662

    
663
  def BuildHooksNodes(self):
664
    nodes = self.cfg.GetNodeGroup(self.group_uuid).members
665
    return (nodes, nodes)
666

    
667
  def CheckPrereq(self):
668
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
669

    
670
    assert self.group_uuid in owned_groups
671

    
672
    # Check if locked instances are still correct
673
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
674
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
675

    
676
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
677
    self.connected = True
678
    if self.network_uuid not in self.group.networks:
679
      self.LogWarning("Network '%s' is not mapped to group '%s'",
680
                      self.network_name, self.group.name)
681
      self.connected = False
682

    
683
    # We need this check only if network is not already connected
684
    else:
685
      _NetworkConflictCheck(self, lambda nic: nic.network == self.network_uuid,
686
                            "disconnect from", owned_instances)
687

    
688
  def Exec(self, feedback_fn):
689
    # Disconnect the network and update the group only if network is connected
690
    if self.connected:
691
      del self.group.networks[self.network_uuid]
692
      self.cfg.Update(self.group, feedback_fn)