Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / network.py @ c01a7953

History | View | Annotate | Download (21.7 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
    if self.op.network is None:
114
      raise errors.OpPrereqError("Network must be given",
115
                                 errors.ECODE_INVAL)
116

    
117
    try:
118
      existing_uuid = self.cfg.LookupNetwork(self.op.network_name)
119
    except errors.OpPrereqError:
120
      pass
121
    else:
122
      raise errors.OpPrereqError("Desired network name '%s' already exists as a"
123
                                 " network (UUID: %s)" %
124
                                 (self.op.network_name, existing_uuid),
125
                                 errors.ECODE_EXISTS)
126

    
127
    # Check tag validity
128
    for tag in self.op.tags:
129
      objects.TaggableObject.ValidateTag(tag)
130

    
131
    self.nobj = objects.Network(name=self.op.network_name,
132
                                network=self.op.network,
133
                                gateway=self.op.gateway,
134
                                network6=self.op.network6,
135
                                gateway6=self.op.gateway6,
136
                                mac_prefix=self.op.mac_prefix,
137
                                uuid=self.network_uuid)
138

    
139
    # Initialize the associated address pool
140
    try:
141
      self.pool = network.AddressPool.InitializeNetwork(self.nobj)
142
    except errors.AddressPoolError, err:
143
      raise errors.OpPrereqError("Cannot create IP address pool for network"
144
                               " '%s': %s" % (self.op.network_name, err))
145

    
146
  def BuildHooksEnv(self):
147
    """Build hooks env.
148

149
    """
150
    args = {
151
      "name": self.op.network_name,
152
      "subnet": self.op.network,
153
      "gateway": self.op.gateway,
154
      "network6": self.op.network6,
155
      "gateway6": self.op.gateway6,
156
      "mac_prefix": self.op.mac_prefix,
157
      "tags": self.op.tags,
158
      }
159
    return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
160

    
161
  def Exec(self, feedback_fn):
162
    """Add the ip pool to the cluster.
163

164
    """
165
    # Check if we need to reserve the nodes and the cluster master IP
166
    # These may not be allocated to any instances in routed mode, as
167
    # they wouldn't function anyway.
168
    if self.op.conflicts_check:
169
      for node in self.cfg.GetAllNodesInfo().values():
170
        for ip in [node.primary_ip, node.secondary_ip]:
171
          try:
172
            if self.pool.Contains(ip):
173
              self.pool.Reserve(ip, True)
174
              self.LogInfo("Reserved IP address of node '%s' (%s)",
175
                           node.name, ip)
176
          except errors.AddressPoolError, err:
177
            self.LogWarning("Cannot reserve IP address '%s' of node '%s': %s",
178
                            ip, node.name, err)
179

    
180
      master_ip = self.cfg.GetClusterInfo().master_ip
181
      try:
182
        if self.pool.Contains(master_ip):
183
          self.pool.Reserve(master_ip, True)
184
          self.LogInfo("Reserved cluster master IP address (%s)", master_ip)
185
      except errors.AddressPoolError, err:
186
        self.LogWarning("Cannot reserve cluster master IP address (%s): %s",
187
                        master_ip, err)
188

    
189
    if self.op.add_reserved_ips:
190
      for ip in self.op.add_reserved_ips:
191
        try:
192
          self.pool.Reserve(ip, external=True)
193
        except errors.AddressPoolError, err:
194
          raise errors.OpExecError("Cannot reserve IP address '%s': %s" %
195
                                   (ip, err))
196

    
197
    if self.op.tags:
198
      for tag in self.op.tags:
199
        self.nobj.AddTag(tag)
200

    
201
    self.cfg.AddNetwork(self.nobj, self.proc.GetECId(), check_uuid=False)
202
    del self.remove_locks[locking.LEVEL_NETWORK]
203

    
204

    
205
class LUNetworkRemove(LogicalUnit):
206
  HPATH = "network-remove"
207
  HTYPE = constants.HTYPE_NETWORK
208
  REQ_BGL = False
209

    
210
  def ExpandNames(self):
211
    self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
212

    
213
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
214
    self.needed_locks = {
215
      locking.LEVEL_NETWORK: [self.network_uuid],
216
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
217
      }
218

    
219
  def CheckPrereq(self):
220
    """Check prerequisites.
221

222
    This checks that the given network name exists as a network, that is
223
    empty (i.e., contains no nodes), and that is not the last group of the
224
    cluster.
225

226
    """
227
    # Verify that the network is not conncted.
228
    node_groups = [group.name
229
                   for group in self.cfg.GetAllNodeGroupsInfo().values()
230
                   if self.network_uuid in group.networks]
231

    
232
    if node_groups:
233
      self.LogWarning("Network '%s' is connected to the following"
234
                      " node groups: %s" %
235
                      (self.op.network_name,
236
                       utils.CommaJoin(utils.NiceSort(node_groups))))
237
      raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
238

    
239
  def BuildHooksEnv(self):
240
    """Build hooks env.
241

242
    """
243
    return {
244
      "NETWORK_NAME": self.op.network_name,
245
      }
246

    
247
  def BuildHooksNodes(self):
248
    """Build hooks nodes.
249

250
    """
251
    mn = self.cfg.GetMasterNode()
252
    return ([mn], [mn])
253

    
254
  def Exec(self, feedback_fn):
255
    """Remove the network.
256

257
    """
258
    try:
259
      self.cfg.RemoveNetwork(self.network_uuid)
260
    except errors.ConfigurationError:
261
      raise errors.OpExecError("Network '%s' with UUID %s disappeared" %
262
                               (self.op.network_name, self.network_uuid))
263

    
264

    
265
class LUNetworkSetParams(LogicalUnit):
266
  """Modifies the parameters of a network.
267

268
  """
269
  HPATH = "network-modify"
270
  HTYPE = constants.HTYPE_NETWORK
271
  REQ_BGL = False
272

    
273
  def CheckArguments(self):
274
    if (self.op.gateway and
275
        (self.op.add_reserved_ips or self.op.remove_reserved_ips)):
276
      raise errors.OpPrereqError("Cannot modify gateway and reserved ips"
277
                                 " at once", errors.ECODE_INVAL)
278

    
279
  def ExpandNames(self):
280
    self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
281

    
282
    self.needed_locks = {
283
      locking.LEVEL_NETWORK: [self.network_uuid],
284
      }
285

    
286
  def CheckPrereq(self):
287
    """Check prerequisites.
288

289
    """
290
    self.network = self.cfg.GetNetwork(self.network_uuid)
291
    self.gateway = self.network.gateway
292
    self.mac_prefix = self.network.mac_prefix
293
    self.network6 = self.network.network6
294
    self.gateway6 = self.network.gateway6
295
    self.tags = self.network.tags
296

    
297
    self.pool = network.AddressPool(self.network)
298

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

    
305
    if self.op.mac_prefix:
306
      if self.op.mac_prefix == constants.VALUE_NONE:
307
        self.mac_prefix = None
308
      else:
309
        self.mac_prefix = \
310
          utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
311

    
312
    if self.op.gateway6:
313
      if self.op.gateway6 == constants.VALUE_NONE:
314
        self.gateway6 = None
315
      else:
316
        self.gateway6 = self.op.gateway6
317

    
318
    if self.op.network6:
319
      if self.op.network6 == constants.VALUE_NONE:
320
        self.network6 = None
321
      else:
322
        self.network6 = self.op.network6
323

    
324
  def BuildHooksEnv(self):
325
    """Build hooks env.
326

327
    """
328
    args = {
329
      "name": self.op.network_name,
330
      "subnet": self.network.network,
331
      "gateway": self.gateway,
332
      "network6": self.network6,
333
      "gateway6": self.gateway6,
334
      "mac_prefix": self.mac_prefix,
335
      "tags": self.tags,
336
      }
337
    return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
338

    
339
  def BuildHooksNodes(self):
340
    """Build hooks nodes.
341

342
    """
343
    mn = self.cfg.GetMasterNode()
344
    return ([mn], [mn])
345

    
346
  def Exec(self, feedback_fn):
347
    """Modifies the network.
348

349
    """
350
    #TODO: reserve/release via temporary reservation manager
351
    #      extend cfg.ReserveIp/ReleaseIp with the external flag
352
    ec_id = self.proc.GetECId()
353
    if self.op.gateway:
354
      if self.gateway == self.network.gateway:
355
        self.LogWarning("Gateway is already %s", self.gateway)
356
      else:
357
        if self.gateway:
358
          self.cfg.ReserveIp(self.network_uuid, self.gateway, True, ec_id)
359
        if self.network.gateway:
360
          self.cfg.ReleaseIp(self.network_uuid, self.network.gateway, True, ec_id)
361
        self.network.gateway = self.gateway
362

    
363
    if self.op.add_reserved_ips:
364
      for ip in self.op.add_reserved_ips:
365
        self.cfg.ReserveIp(self.network_uuid, ip, True, ec_id)
366

    
367
    if self.op.remove_reserved_ips:
368
      for ip in self.op.remove_reserved_ips:
369
        if ip == self.network.gateway:
370
          self.LogWarning("Cannot unreserve Gateway's IP")
371
          continue
372
        self.cfg.ReleaseIp(self.network_uuid, ip, True, ec_id)
373

    
374
    if self.op.mac_prefix:
375
      self.network.mac_prefix = self.mac_prefix
376

    
377
    if self.op.network6:
378
      self.network.network6 = self.network6
379

    
380
    if self.op.gateway6:
381
      self.network.gateway6 = self.gateway6
382

    
383
    self.pool.Validate()
384

    
385
    self.cfg.Update(self.network, feedback_fn)
386

    
387

    
388
class NetworkQuery(QueryBase):
389
  FIELDS = query.NETWORK_FIELDS
390

    
391
  def ExpandNames(self, lu):
392
    lu.needed_locks = {}
393
    lu.share_locks = ShareAll()
394

    
395
    self.do_locking = self.use_locking
396

    
397
    all_networks = lu.cfg.GetAllNetworksInfo()
398
    name_to_uuid = dict((n.name, n.uuid) for n in all_networks.values())
399

    
400
    if self.names:
401
      missing = []
402
      self.wanted = []
403

    
404
      for name in self.names:
405
        if name in name_to_uuid:
406
          self.wanted.append(name_to_uuid[name])
407
        else:
408
          missing.append(name)
409

    
410
      if missing:
411
        raise errors.OpPrereqError("Some networks do not exist: %s" % missing,
412
                                   errors.ECODE_NOENT)
413
    else:
414
      self.wanted = locking.ALL_SET
415

    
416
    if self.do_locking:
417
      lu.needed_locks[locking.LEVEL_NETWORK] = self.wanted
418
      if query.NETQ_INST in self.requested_data:
419
        lu.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
420
      if query.NETQ_GROUP in self.requested_data:
421
        lu.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET
422

    
423
  def DeclareLocks(self, lu, level):
424
    pass
425

    
426
  def _GetQueryData(self, lu):
427
    """Computes the list of networks and their attributes.
428

429
    """
430
    all_networks = lu.cfg.GetAllNetworksInfo()
431

    
432
    network_uuids = self._GetNames(lu, all_networks.keys(),
433
                                   locking.LEVEL_NETWORK)
434

    
435
    do_instances = query.NETQ_INST in self.requested_data
436
    do_groups = query.NETQ_GROUP in self.requested_data
437

    
438
    network_to_instances = None
439
    network_to_groups = None
440

    
441
    # For NETQ_GROUP, we need to map network->[groups]
442
    if do_groups:
443
      all_groups = lu.cfg.GetAllNodeGroupsInfo()
444
      network_to_groups = dict((uuid, []) for uuid in network_uuids)
445
      for _, group in all_groups.iteritems():
446
        for net_uuid in network_uuids:
447
          netparams = group.networks.get(net_uuid, None)
448
          if netparams:
449
            info = (group.name, netparams[constants.NIC_MODE],
450
                    netparams[constants.NIC_LINK])
451

    
452
            network_to_groups[net_uuid].append(info)
453

    
454
    if do_instances:
455
      all_instances = lu.cfg.GetAllInstancesInfo()
456
      network_to_instances = dict((uuid, []) for uuid in network_uuids)
457
      for instance in all_instances.values():
458
        for nic in instance.nics:
459
          if nic.network in network_uuids:
460
            network_to_instances[nic.network].append(instance.name)
461
            break
462

    
463
    if query.NETQ_STATS in self.requested_data:
464
      stats = \
465
        dict((uuid,
466
              self._GetStats(network.AddressPool(all_networks[uuid])))
467
             for uuid in network_uuids)
468
    else:
469
      stats = None
470

    
471
    return query.NetworkQueryData([all_networks[uuid]
472
                                   for uuid in network_uuids],
473
                                   network_to_groups,
474
                                   network_to_instances,
475
                                   stats)
476

    
477
  @staticmethod
478
  def _GetStats(pool):
479
    """Returns statistics for a network address pool.
480

481
    """
482
    return {
483
      "free_count": pool.GetFreeCount(),
484
      "reserved_count": pool.GetReservedCount(),
485
      "map": pool.GetMap(),
486
      "external_reservations":
487
        utils.CommaJoin(pool.GetExternalReservations()),
488
      }
489

    
490

    
491
class LUNetworkQuery(NoHooksLU):
492
  """Logical unit for querying networks.
493

494
  """
495
  REQ_BGL = False
496

    
497
  def CheckArguments(self):
498
    self.nq = NetworkQuery(qlang.MakeSimpleFilter("name", self.op.names),
499
                            self.op.output_fields, self.op.use_locking)
500

    
501
  def ExpandNames(self):
502
    self.nq.ExpandNames(self)
503

    
504
  def Exec(self, feedback_fn):
505
    return self.nq.OldStyleQuery(self)
506

    
507

    
508
def _FmtNetworkConflict(details):
509
  """Utility for L{_NetworkConflictCheck}.
510

511
  """
512
  return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
513
                         for (idx, ipaddr) in details)
514

    
515

    
516
def _NetworkConflictCheck(lu, check_fn, action, instances):
517
  """Checks for network interface conflicts with a network.
518

519
  @type lu: L{LogicalUnit}
520
  @type check_fn: callable receiving one parameter (L{objects.NIC}) and
521
    returning boolean
522
  @param check_fn: Function checking for conflict
523
  @type action: string
524
  @param action: Part of error message (see code)
525
  @raise errors.OpPrereqError: If conflicting IP addresses are found.
526

527
  """
528
  conflicts = []
529

    
530
  for (_, instance) in lu.cfg.GetMultiInstanceInfo(instances):
531
    instconflicts = [(idx, nic.ip)
532
                     for (idx, nic) in enumerate(instance.nics)
533
                     if check_fn(nic)]
534

    
535
    if instconflicts:
536
      conflicts.append((instance.name, instconflicts))
537

    
538
  if conflicts:
539
    lu.LogWarning("IP addresses from network '%s', which is about to %s"
540
                  " node group '%s', are in use: %s" %
541
                  (lu.network_name, action, lu.group.name,
542
                   utils.CommaJoin(("%s: %s" %
543
                                    (name, _FmtNetworkConflict(details)))
544
                                   for (name, details) in conflicts)))
545

    
546
    raise errors.OpPrereqError("Conflicting IP addresses found; "
547
                               " remove/modify the corresponding network"
548
                               " interfaces", errors.ECODE_STATE)
549

    
550

    
551
class LUNetworkConnect(LogicalUnit):
552
  """Connect a network to a nodegroup
553

554
  """
555
  HPATH = "network-connect"
556
  HTYPE = constants.HTYPE_NETWORK
557
  REQ_BGL = False
558

    
559
  def ExpandNames(self):
560
    self.network_name = self.op.network_name
561
    self.group_name = self.op.group_name
562
    self.network_mode = self.op.network_mode
563
    self.network_link = self.op.network_link
564

    
565
    self.network_uuid = self.cfg.LookupNetwork(self.network_name)
566
    self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
567

    
568
    self.needed_locks = {
569
      locking.LEVEL_INSTANCE: [],
570
      locking.LEVEL_NODEGROUP: [self.group_uuid],
571
      }
572
    self.share_locks[locking.LEVEL_INSTANCE] = 1
573

    
574
    if self.op.conflicts_check:
575
      self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
576
      self.share_locks[locking.LEVEL_NETWORK] = 1
577

    
578
  def DeclareLocks(self, level):
579
    if level == locking.LEVEL_INSTANCE:
580
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
581

    
582
      # Lock instances optimistically, needs verification once group lock has
583
      # been acquired
584
      if self.op.conflicts_check:
585
        self.needed_locks[locking.LEVEL_INSTANCE] = \
586
            self.cfg.GetNodeGroupInstances(self.group_uuid)
587

    
588
  def BuildHooksEnv(self):
589
    ret = {
590
      "GROUP_NAME": self.group_name,
591
      "GROUP_NETWORK_MODE": self.network_mode,
592
      "GROUP_NETWORK_LINK": self.network_link,
593
      }
594
    return ret
595

    
596
  def BuildHooksNodes(self):
597
    nodes = self.cfg.GetNodeGroup(self.group_uuid).members
598
    return (nodes, nodes)
599

    
600
  def CheckPrereq(self):
601
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
602

    
603
    assert self.group_uuid in owned_groups
604

    
605
    # Check if locked instances are still correct
606
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
607
    if self.op.conflicts_check:
608
      CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
609

    
610
    self.netparams = {
611
      constants.NIC_MODE: self.network_mode,
612
      constants.NIC_LINK: self.network_link,
613
      }
614
    objects.NIC.CheckParameterSyntax(self.netparams)
615

    
616
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
617
    #if self.network_mode == constants.NIC_MODE_BRIDGED:
618
    #  _CheckNodeGroupBridgesExist(self, self.network_link, self.group_uuid)
619
    self.connected = False
620
    if self.network_uuid in self.group.networks:
621
      self.LogWarning("Network '%s' is already mapped to group '%s'" %
622
                      (self.network_name, self.group.name))
623
      self.connected = True
624

    
625
    # check only if not already connected
626
    elif self.op.conflicts_check:
627
      pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
628

    
629
      _NetworkConflictCheck(self, lambda nic: pool.Contains(nic.ip),
630
                            "connect to", owned_instances)
631

    
632
  def Exec(self, feedback_fn):
633
    # Connect the network and update the group only if not already connected
634
    if not self.connected:
635
      self.group.networks[self.network_uuid] = self.netparams
636
      self.cfg.Update(self.group, feedback_fn)
637

    
638

    
639
class LUNetworkDisconnect(LogicalUnit):
640
  """Disconnect a network to a nodegroup
641

642
  """
643
  HPATH = "network-disconnect"
644
  HTYPE = constants.HTYPE_NETWORK
645
  REQ_BGL = False
646

    
647
  def ExpandNames(self):
648
    self.network_name = self.op.network_name
649
    self.group_name = self.op.group_name
650

    
651
    self.network_uuid = self.cfg.LookupNetwork(self.network_name)
652
    self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
653

    
654
    self.needed_locks = {
655
      locking.LEVEL_INSTANCE: [],
656
      locking.LEVEL_NODEGROUP: [self.group_uuid],
657
      }
658
    self.share_locks[locking.LEVEL_INSTANCE] = 1
659

    
660
  def DeclareLocks(self, level):
661
    if level == locking.LEVEL_INSTANCE:
662
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
663

    
664
      # Lock instances optimistically, needs verification once group lock has
665
      # been acquired
666
      self.needed_locks[locking.LEVEL_INSTANCE] = \
667
        self.cfg.GetNodeGroupInstances(self.group_uuid)
668

    
669
  def BuildHooksEnv(self):
670
    ret = {
671
      "GROUP_NAME": self.group_name,
672
      }
673
    return ret
674

    
675
  def BuildHooksNodes(self):
676
    nodes = self.cfg.GetNodeGroup(self.group_uuid).members
677
    return (nodes, nodes)
678

    
679
  def CheckPrereq(self):
680
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
681

    
682
    assert self.group_uuid in owned_groups
683

    
684
    # Check if locked instances are still correct
685
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
686
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
687

    
688
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
689
    self.connected = True
690
    if self.network_uuid not in self.group.networks:
691
      self.LogWarning("Network '%s' is not mapped to group '%s'",
692
                      self.network_name, self.group.name)
693
      self.connected = False
694

    
695
    # We need this check only if network is not already connected
696
    else:
697
      _NetworkConflictCheck(self, lambda nic: nic.network == self.network_uuid,
698
                            "disconnect from", owned_instances)
699

    
700
  def Exec(self, feedback_fn):
701
    # Disconnect the network and update the group only if network is connected
702
    if self.connected:
703
      del self.group.networks[self.network_uuid]
704
      self.cfg.Update(self.group, feedback_fn)