Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / network.py @ 4b700ca7

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.NetworkError, 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.network = self.nobj.network
276
    self.gateway = self.nobj.gateway
277
    self.mac_prefix = self.nobj.mac_prefix
278
    self.network6 = self.nobj.network6
279
    self.gateway6 = self.nobj.gateway6
280
    self.tags = self.nobj.tags
281

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

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

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

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

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

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

    
311
    network.Network.Check(self.gateway6, self.network6)
312

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

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

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

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

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

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

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

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

    
364
    if self.op.mac_prefix:
365
      self.nobj.mac_prefix = self.mac_prefix
366

    
367
    if self.op.network6:
368
      self.nobj.network6 = self.network6
369

    
370
    if self.op.gateway6:
371
      self.nobj.gateway6 = self.gateway6
372

    
373
    self.cfg.Update(self.nobj, feedback_fn)
374

    
375

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

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

    
383
    self.do_locking = self.use_locking
384

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

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

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

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

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

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

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

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

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

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

    
426
    network_to_instances = None
427
    network_to_groups = None
428

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

    
440
            network_to_groups[net_uuid].append(info)
441

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

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

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

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

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

    
477

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

481
  """
482
  REQ_BGL = False
483

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

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

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

    
494

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

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

    
502

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

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

514
  """
515
  conflicts = []
516

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

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

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

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

    
537

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

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

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

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

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

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

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

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

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

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

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

    
590
    assert self.group_uuid in owned_groups
591

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

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

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

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

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

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

    
625

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

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

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

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

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

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

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

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

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

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

    
669
    assert self.group_uuid in owned_groups
670

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

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

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

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