Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / network.py @ 44ffd981

History | View | Annotate | Download (19.5 kB)

1 37dc17e3 Thomas Thrainer
#
2 37dc17e3 Thomas Thrainer
#
3 37dc17e3 Thomas Thrainer
4 37dc17e3 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 37dc17e3 Thomas Thrainer
#
6 37dc17e3 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 37dc17e3 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 37dc17e3 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 37dc17e3 Thomas Thrainer
# (at your option) any later version.
10 37dc17e3 Thomas Thrainer
#
11 37dc17e3 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 37dc17e3 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 37dc17e3 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 37dc17e3 Thomas Thrainer
# General Public License for more details.
15 37dc17e3 Thomas Thrainer
#
16 37dc17e3 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 37dc17e3 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 37dc17e3 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 37dc17e3 Thomas Thrainer
# 02110-1301, USA.
20 37dc17e3 Thomas Thrainer
21 37dc17e3 Thomas Thrainer
22 37dc17e3 Thomas Thrainer
"""Logical units dealing with networks."""
23 37dc17e3 Thomas Thrainer
24 37dc17e3 Thomas Thrainer
from ganeti import constants
25 37dc17e3 Thomas Thrainer
from ganeti import errors
26 37dc17e3 Thomas Thrainer
from ganeti import locking
27 37dc17e3 Thomas Thrainer
from ganeti import network
28 37dc17e3 Thomas Thrainer
from ganeti import objects
29 37dc17e3 Thomas Thrainer
from ganeti import query
30 37dc17e3 Thomas Thrainer
from ganeti import utils
31 5eacbcae Thomas Thrainer
from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase
32 44ffd981 Helga Velroyen
from ganeti.cmdlib.common import CheckNodeGroupInstances
33 37dc17e3 Thomas Thrainer
34 37dc17e3 Thomas Thrainer
35 37dc17e3 Thomas Thrainer
def _BuildNetworkHookEnv(name, subnet, gateway, network6, gateway6,
36 37dc17e3 Thomas Thrainer
                         mac_prefix, tags):
37 37dc17e3 Thomas Thrainer
  """Builds network related env variables for hooks
38 37dc17e3 Thomas Thrainer

39 37dc17e3 Thomas Thrainer
  This builds the hook environment from individual variables.
40 37dc17e3 Thomas Thrainer

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

56 37dc17e3 Thomas Thrainer
  """
57 37dc17e3 Thomas Thrainer
  env = {}
58 37dc17e3 Thomas Thrainer
  if name:
59 37dc17e3 Thomas Thrainer
    env["NETWORK_NAME"] = name
60 37dc17e3 Thomas Thrainer
  if subnet:
61 37dc17e3 Thomas Thrainer
    env["NETWORK_SUBNET"] = subnet
62 37dc17e3 Thomas Thrainer
  if gateway:
63 37dc17e3 Thomas Thrainer
    env["NETWORK_GATEWAY"] = gateway
64 37dc17e3 Thomas Thrainer
  if network6:
65 37dc17e3 Thomas Thrainer
    env["NETWORK_SUBNET6"] = network6
66 37dc17e3 Thomas Thrainer
  if gateway6:
67 37dc17e3 Thomas Thrainer
    env["NETWORK_GATEWAY6"] = gateway6
68 37dc17e3 Thomas Thrainer
  if mac_prefix:
69 37dc17e3 Thomas Thrainer
    env["NETWORK_MAC_PREFIX"] = mac_prefix
70 37dc17e3 Thomas Thrainer
  if tags:
71 37dc17e3 Thomas Thrainer
    env["NETWORK_TAGS"] = " ".join(tags)
72 37dc17e3 Thomas Thrainer
73 37dc17e3 Thomas Thrainer
  return env
74 37dc17e3 Thomas Thrainer
75 37dc17e3 Thomas Thrainer
76 37dc17e3 Thomas Thrainer
class LUNetworkAdd(LogicalUnit):
77 37dc17e3 Thomas Thrainer
  """Logical unit for creating networks.
78 37dc17e3 Thomas Thrainer

79 37dc17e3 Thomas Thrainer
  """
80 37dc17e3 Thomas Thrainer
  HPATH = "network-add"
81 37dc17e3 Thomas Thrainer
  HTYPE = constants.HTYPE_NETWORK
82 37dc17e3 Thomas Thrainer
  REQ_BGL = False
83 37dc17e3 Thomas Thrainer
84 37dc17e3 Thomas Thrainer
  def BuildHooksNodes(self):
85 37dc17e3 Thomas Thrainer
    """Build hooks nodes.
86 37dc17e3 Thomas Thrainer

87 37dc17e3 Thomas Thrainer
    """
88 37dc17e3 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
89 37dc17e3 Thomas Thrainer
    return ([mn], [mn])
90 37dc17e3 Thomas Thrainer
91 37dc17e3 Thomas Thrainer
  def CheckArguments(self):
92 37dc17e3 Thomas Thrainer
    if self.op.mac_prefix:
93 37dc17e3 Thomas Thrainer
      self.op.mac_prefix = \
94 37dc17e3 Thomas Thrainer
        utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
95 37dc17e3 Thomas Thrainer
96 37dc17e3 Thomas Thrainer
  def ExpandNames(self):
97 37dc17e3 Thomas Thrainer
    self.network_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
98 37dc17e3 Thomas Thrainer
99 37dc17e3 Thomas Thrainer
    if self.op.conflicts_check:
100 37dc17e3 Thomas Thrainer
      self.share_locks[locking.LEVEL_NODE] = 1
101 37dc17e3 Thomas Thrainer
      self.share_locks[locking.LEVEL_NODE_ALLOC] = 1
102 37dc17e3 Thomas Thrainer
      self.needed_locks = {
103 37dc17e3 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
104 37dc17e3 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
105 37dc17e3 Thomas Thrainer
        }
106 37dc17e3 Thomas Thrainer
    else:
107 37dc17e3 Thomas Thrainer
      self.needed_locks = {}
108 37dc17e3 Thomas Thrainer
109 37dc17e3 Thomas Thrainer
    self.add_locks[locking.LEVEL_NETWORK] = self.network_uuid
110 37dc17e3 Thomas Thrainer
111 37dc17e3 Thomas Thrainer
  def CheckPrereq(self):
112 37dc17e3 Thomas Thrainer
    if self.op.network is None:
113 37dc17e3 Thomas Thrainer
      raise errors.OpPrereqError("Network must be given",
114 37dc17e3 Thomas Thrainer
                                 errors.ECODE_INVAL)
115 37dc17e3 Thomas Thrainer
116 37dc17e3 Thomas Thrainer
    try:
117 37dc17e3 Thomas Thrainer
      existing_uuid = self.cfg.LookupNetwork(self.op.network_name)
118 37dc17e3 Thomas Thrainer
    except errors.OpPrereqError:
119 37dc17e3 Thomas Thrainer
      pass
120 37dc17e3 Thomas Thrainer
    else:
121 37dc17e3 Thomas Thrainer
      raise errors.OpPrereqError("Desired network name '%s' already exists as a"
122 37dc17e3 Thomas Thrainer
                                 " network (UUID: %s)" %
123 37dc17e3 Thomas Thrainer
                                 (self.op.network_name, existing_uuid),
124 37dc17e3 Thomas Thrainer
                                 errors.ECODE_EXISTS)
125 37dc17e3 Thomas Thrainer
126 37dc17e3 Thomas Thrainer
    # Check tag validity
127 37dc17e3 Thomas Thrainer
    for tag in self.op.tags:
128 37dc17e3 Thomas Thrainer
      objects.TaggableObject.ValidateTag(tag)
129 37dc17e3 Thomas Thrainer
130 37dc17e3 Thomas Thrainer
  def BuildHooksEnv(self):
131 37dc17e3 Thomas Thrainer
    """Build hooks env.
132 37dc17e3 Thomas Thrainer

133 37dc17e3 Thomas Thrainer
    """
134 37dc17e3 Thomas Thrainer
    args = {
135 37dc17e3 Thomas Thrainer
      "name": self.op.network_name,
136 37dc17e3 Thomas Thrainer
      "subnet": self.op.network,
137 37dc17e3 Thomas Thrainer
      "gateway": self.op.gateway,
138 37dc17e3 Thomas Thrainer
      "network6": self.op.network6,
139 37dc17e3 Thomas Thrainer
      "gateway6": self.op.gateway6,
140 37dc17e3 Thomas Thrainer
      "mac_prefix": self.op.mac_prefix,
141 37dc17e3 Thomas Thrainer
      "tags": self.op.tags,
142 37dc17e3 Thomas Thrainer
      }
143 37dc17e3 Thomas Thrainer
    return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
144 37dc17e3 Thomas Thrainer
145 37dc17e3 Thomas Thrainer
  def Exec(self, feedback_fn):
146 37dc17e3 Thomas Thrainer
    """Add the ip pool to the cluster.
147 37dc17e3 Thomas Thrainer

148 37dc17e3 Thomas Thrainer
    """
149 37dc17e3 Thomas Thrainer
    nobj = objects.Network(name=self.op.network_name,
150 37dc17e3 Thomas Thrainer
                           network=self.op.network,
151 37dc17e3 Thomas Thrainer
                           gateway=self.op.gateway,
152 37dc17e3 Thomas Thrainer
                           network6=self.op.network6,
153 37dc17e3 Thomas Thrainer
                           gateway6=self.op.gateway6,
154 37dc17e3 Thomas Thrainer
                           mac_prefix=self.op.mac_prefix,
155 37dc17e3 Thomas Thrainer
                           uuid=self.network_uuid)
156 37dc17e3 Thomas Thrainer
    # Initialize the associated address pool
157 37dc17e3 Thomas Thrainer
    try:
158 37dc17e3 Thomas Thrainer
      pool = network.AddressPool.InitializeNetwork(nobj)
159 37dc17e3 Thomas Thrainer
    except errors.AddressPoolError, err:
160 37dc17e3 Thomas Thrainer
      raise errors.OpExecError("Cannot create IP address pool for network"
161 37dc17e3 Thomas Thrainer
                               " '%s': %s" % (self.op.network_name, err))
162 37dc17e3 Thomas Thrainer
163 37dc17e3 Thomas Thrainer
    # Check if we need to reserve the nodes and the cluster master IP
164 37dc17e3 Thomas Thrainer
    # These may not be allocated to any instances in routed mode, as
165 37dc17e3 Thomas Thrainer
    # they wouldn't function anyway.
166 37dc17e3 Thomas Thrainer
    if self.op.conflicts_check:
167 37dc17e3 Thomas Thrainer
      for node in self.cfg.GetAllNodesInfo().values():
168 37dc17e3 Thomas Thrainer
        for ip in [node.primary_ip, node.secondary_ip]:
169 37dc17e3 Thomas Thrainer
          try:
170 37dc17e3 Thomas Thrainer
            if pool.Contains(ip):
171 37dc17e3 Thomas Thrainer
              pool.Reserve(ip)
172 37dc17e3 Thomas Thrainer
              self.LogInfo("Reserved IP address of node '%s' (%s)",
173 37dc17e3 Thomas Thrainer
                           node.name, ip)
174 37dc17e3 Thomas Thrainer
          except errors.AddressPoolError, err:
175 37dc17e3 Thomas Thrainer
            self.LogWarning("Cannot reserve IP address '%s' of node '%s': %s",
176 37dc17e3 Thomas Thrainer
                            ip, node.name, err)
177 37dc17e3 Thomas Thrainer
178 37dc17e3 Thomas Thrainer
      master_ip = self.cfg.GetClusterInfo().master_ip
179 37dc17e3 Thomas Thrainer
      try:
180 37dc17e3 Thomas Thrainer
        if pool.Contains(master_ip):
181 37dc17e3 Thomas Thrainer
          pool.Reserve(master_ip)
182 37dc17e3 Thomas Thrainer
          self.LogInfo("Reserved cluster master IP address (%s)", master_ip)
183 37dc17e3 Thomas Thrainer
      except errors.AddressPoolError, err:
184 37dc17e3 Thomas Thrainer
        self.LogWarning("Cannot reserve cluster master IP address (%s): %s",
185 37dc17e3 Thomas Thrainer
                        master_ip, err)
186 37dc17e3 Thomas Thrainer
187 37dc17e3 Thomas Thrainer
    if self.op.add_reserved_ips:
188 37dc17e3 Thomas Thrainer
      for ip in self.op.add_reserved_ips:
189 37dc17e3 Thomas Thrainer
        try:
190 37dc17e3 Thomas Thrainer
          pool.Reserve(ip, external=True)
191 37dc17e3 Thomas Thrainer
        except errors.AddressPoolError, err:
192 37dc17e3 Thomas Thrainer
          raise errors.OpExecError("Cannot reserve IP address '%s': %s" %
193 37dc17e3 Thomas Thrainer
                                   (ip, err))
194 37dc17e3 Thomas Thrainer
195 37dc17e3 Thomas Thrainer
    if self.op.tags:
196 37dc17e3 Thomas Thrainer
      for tag in self.op.tags:
197 37dc17e3 Thomas Thrainer
        nobj.AddTag(tag)
198 37dc17e3 Thomas Thrainer
199 37dc17e3 Thomas Thrainer
    self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False)
200 37dc17e3 Thomas Thrainer
    del self.remove_locks[locking.LEVEL_NETWORK]
201 37dc17e3 Thomas Thrainer
202 37dc17e3 Thomas Thrainer
203 37dc17e3 Thomas Thrainer
class LUNetworkRemove(LogicalUnit):
204 37dc17e3 Thomas Thrainer
  HPATH = "network-remove"
205 37dc17e3 Thomas Thrainer
  HTYPE = constants.HTYPE_NETWORK
206 37dc17e3 Thomas Thrainer
  REQ_BGL = False
207 37dc17e3 Thomas Thrainer
208 37dc17e3 Thomas Thrainer
  def ExpandNames(self):
209 37dc17e3 Thomas Thrainer
    self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
210 37dc17e3 Thomas Thrainer
211 37dc17e3 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
212 37dc17e3 Thomas Thrainer
    self.needed_locks = {
213 37dc17e3 Thomas Thrainer
      locking.LEVEL_NETWORK: [self.network_uuid],
214 37dc17e3 Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
215 37dc17e3 Thomas Thrainer
      }
216 37dc17e3 Thomas Thrainer
217 37dc17e3 Thomas Thrainer
  def CheckPrereq(self):
218 37dc17e3 Thomas Thrainer
    """Check prerequisites.
219 37dc17e3 Thomas Thrainer

220 37dc17e3 Thomas Thrainer
    This checks that the given network name exists as a network, that is
221 37dc17e3 Thomas Thrainer
    empty (i.e., contains no nodes), and that is not the last group of the
222 37dc17e3 Thomas Thrainer
    cluster.
223 37dc17e3 Thomas Thrainer

224 37dc17e3 Thomas Thrainer
    """
225 37dc17e3 Thomas Thrainer
    # Verify that the network is not conncted.
226 37dc17e3 Thomas Thrainer
    node_groups = [group.name
227 37dc17e3 Thomas Thrainer
                   for group in self.cfg.GetAllNodeGroupsInfo().values()
228 37dc17e3 Thomas Thrainer
                   if self.network_uuid in group.networks]
229 37dc17e3 Thomas Thrainer
230 37dc17e3 Thomas Thrainer
    if node_groups:
231 37dc17e3 Thomas Thrainer
      self.LogWarning("Network '%s' is connected to the following"
232 37dc17e3 Thomas Thrainer
                      " node groups: %s" %
233 37dc17e3 Thomas Thrainer
                      (self.op.network_name,
234 37dc17e3 Thomas Thrainer
                       utils.CommaJoin(utils.NiceSort(node_groups))))
235 37dc17e3 Thomas Thrainer
      raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
236 37dc17e3 Thomas Thrainer
237 37dc17e3 Thomas Thrainer
  def BuildHooksEnv(self):
238 37dc17e3 Thomas Thrainer
    """Build hooks env.
239 37dc17e3 Thomas Thrainer

240 37dc17e3 Thomas Thrainer
    """
241 37dc17e3 Thomas Thrainer
    return {
242 37dc17e3 Thomas Thrainer
      "NETWORK_NAME": self.op.network_name,
243 37dc17e3 Thomas Thrainer
      }
244 37dc17e3 Thomas Thrainer
245 37dc17e3 Thomas Thrainer
  def BuildHooksNodes(self):
246 37dc17e3 Thomas Thrainer
    """Build hooks nodes.
247 37dc17e3 Thomas Thrainer

248 37dc17e3 Thomas Thrainer
    """
249 37dc17e3 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
250 37dc17e3 Thomas Thrainer
    return ([mn], [mn])
251 37dc17e3 Thomas Thrainer
252 37dc17e3 Thomas Thrainer
  def Exec(self, feedback_fn):
253 37dc17e3 Thomas Thrainer
    """Remove the network.
254 37dc17e3 Thomas Thrainer

255 37dc17e3 Thomas Thrainer
    """
256 37dc17e3 Thomas Thrainer
    try:
257 37dc17e3 Thomas Thrainer
      self.cfg.RemoveNetwork(self.network_uuid)
258 37dc17e3 Thomas Thrainer
    except errors.ConfigurationError:
259 37dc17e3 Thomas Thrainer
      raise errors.OpExecError("Network '%s' with UUID %s disappeared" %
260 37dc17e3 Thomas Thrainer
                               (self.op.network_name, self.network_uuid))
261 37dc17e3 Thomas Thrainer
262 37dc17e3 Thomas Thrainer
263 37dc17e3 Thomas Thrainer
class LUNetworkSetParams(LogicalUnit):
264 37dc17e3 Thomas Thrainer
  """Modifies the parameters of a network.
265 37dc17e3 Thomas Thrainer

266 37dc17e3 Thomas Thrainer
  """
267 37dc17e3 Thomas Thrainer
  HPATH = "network-modify"
268 37dc17e3 Thomas Thrainer
  HTYPE = constants.HTYPE_NETWORK
269 37dc17e3 Thomas Thrainer
  REQ_BGL = False
270 37dc17e3 Thomas Thrainer
271 37dc17e3 Thomas Thrainer
  def CheckArguments(self):
272 37dc17e3 Thomas Thrainer
    if (self.op.gateway and
273 37dc17e3 Thomas Thrainer
        (self.op.add_reserved_ips or self.op.remove_reserved_ips)):
274 37dc17e3 Thomas Thrainer
      raise errors.OpPrereqError("Cannot modify gateway and reserved ips"
275 37dc17e3 Thomas Thrainer
                                 " at once", errors.ECODE_INVAL)
276 37dc17e3 Thomas Thrainer
277 37dc17e3 Thomas Thrainer
  def ExpandNames(self):
278 37dc17e3 Thomas Thrainer
    self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
279 37dc17e3 Thomas Thrainer
280 37dc17e3 Thomas Thrainer
    self.needed_locks = {
281 37dc17e3 Thomas Thrainer
      locking.LEVEL_NETWORK: [self.network_uuid],
282 37dc17e3 Thomas Thrainer
      }
283 37dc17e3 Thomas Thrainer
284 37dc17e3 Thomas Thrainer
  def CheckPrereq(self):
285 37dc17e3 Thomas Thrainer
    """Check prerequisites.
286 37dc17e3 Thomas Thrainer

287 37dc17e3 Thomas Thrainer
    """
288 37dc17e3 Thomas Thrainer
    self.network = self.cfg.GetNetwork(self.network_uuid)
289 37dc17e3 Thomas Thrainer
    self.gateway = self.network.gateway
290 37dc17e3 Thomas Thrainer
    self.mac_prefix = self.network.mac_prefix
291 37dc17e3 Thomas Thrainer
    self.network6 = self.network.network6
292 37dc17e3 Thomas Thrainer
    self.gateway6 = self.network.gateway6
293 37dc17e3 Thomas Thrainer
    self.tags = self.network.tags
294 37dc17e3 Thomas Thrainer
295 37dc17e3 Thomas Thrainer
    self.pool = network.AddressPool(self.network)
296 37dc17e3 Thomas Thrainer
297 37dc17e3 Thomas Thrainer
    if self.op.gateway:
298 37dc17e3 Thomas Thrainer
      if self.op.gateway == constants.VALUE_NONE:
299 37dc17e3 Thomas Thrainer
        self.gateway = None
300 37dc17e3 Thomas Thrainer
      else:
301 37dc17e3 Thomas Thrainer
        self.gateway = self.op.gateway
302 37dc17e3 Thomas Thrainer
        if self.pool.IsReserved(self.gateway):
303 37dc17e3 Thomas Thrainer
          raise errors.OpPrereqError("Gateway IP address '%s' is already"
304 37dc17e3 Thomas Thrainer
                                     " reserved" % self.gateway,
305 37dc17e3 Thomas Thrainer
                                     errors.ECODE_STATE)
306 37dc17e3 Thomas Thrainer
307 37dc17e3 Thomas Thrainer
    if self.op.mac_prefix:
308 37dc17e3 Thomas Thrainer
      if self.op.mac_prefix == constants.VALUE_NONE:
309 37dc17e3 Thomas Thrainer
        self.mac_prefix = None
310 37dc17e3 Thomas Thrainer
      else:
311 37dc17e3 Thomas Thrainer
        self.mac_prefix = \
312 37dc17e3 Thomas Thrainer
          utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
313 37dc17e3 Thomas Thrainer
314 37dc17e3 Thomas Thrainer
    if self.op.gateway6:
315 37dc17e3 Thomas Thrainer
      if self.op.gateway6 == constants.VALUE_NONE:
316 37dc17e3 Thomas Thrainer
        self.gateway6 = None
317 37dc17e3 Thomas Thrainer
      else:
318 37dc17e3 Thomas Thrainer
        self.gateway6 = self.op.gateway6
319 37dc17e3 Thomas Thrainer
320 37dc17e3 Thomas Thrainer
    if self.op.network6:
321 37dc17e3 Thomas Thrainer
      if self.op.network6 == constants.VALUE_NONE:
322 37dc17e3 Thomas Thrainer
        self.network6 = None
323 37dc17e3 Thomas Thrainer
      else:
324 37dc17e3 Thomas Thrainer
        self.network6 = self.op.network6
325 37dc17e3 Thomas Thrainer
326 37dc17e3 Thomas Thrainer
  def BuildHooksEnv(self):
327 37dc17e3 Thomas Thrainer
    """Build hooks env.
328 37dc17e3 Thomas Thrainer

329 37dc17e3 Thomas Thrainer
    """
330 37dc17e3 Thomas Thrainer
    args = {
331 37dc17e3 Thomas Thrainer
      "name": self.op.network_name,
332 37dc17e3 Thomas Thrainer
      "subnet": self.network.network,
333 37dc17e3 Thomas Thrainer
      "gateway": self.gateway,
334 37dc17e3 Thomas Thrainer
      "network6": self.network6,
335 37dc17e3 Thomas Thrainer
      "gateway6": self.gateway6,
336 37dc17e3 Thomas Thrainer
      "mac_prefix": self.mac_prefix,
337 37dc17e3 Thomas Thrainer
      "tags": self.tags,
338 37dc17e3 Thomas Thrainer
      }
339 37dc17e3 Thomas Thrainer
    return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
340 37dc17e3 Thomas Thrainer
341 37dc17e3 Thomas Thrainer
  def BuildHooksNodes(self):
342 37dc17e3 Thomas Thrainer
    """Build hooks nodes.
343 37dc17e3 Thomas Thrainer

344 37dc17e3 Thomas Thrainer
    """
345 37dc17e3 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
346 37dc17e3 Thomas Thrainer
    return ([mn], [mn])
347 37dc17e3 Thomas Thrainer
348 37dc17e3 Thomas Thrainer
  def Exec(self, feedback_fn):
349 37dc17e3 Thomas Thrainer
    """Modifies the network.
350 37dc17e3 Thomas Thrainer

351 37dc17e3 Thomas Thrainer
    """
352 37dc17e3 Thomas Thrainer
    #TODO: reserve/release via temporary reservation manager
353 37dc17e3 Thomas Thrainer
    #      extend cfg.ReserveIp/ReleaseIp with the external flag
354 37dc17e3 Thomas Thrainer
    if self.op.gateway:
355 37dc17e3 Thomas Thrainer
      if self.gateway == self.network.gateway:
356 37dc17e3 Thomas Thrainer
        self.LogWarning("Gateway is already %s", self.gateway)
357 37dc17e3 Thomas Thrainer
      else:
358 37dc17e3 Thomas Thrainer
        if self.gateway:
359 37dc17e3 Thomas Thrainer
          self.pool.Reserve(self.gateway, external=True)
360 37dc17e3 Thomas Thrainer
        if self.network.gateway:
361 37dc17e3 Thomas Thrainer
          self.pool.Release(self.network.gateway, external=True)
362 37dc17e3 Thomas Thrainer
        self.network.gateway = self.gateway
363 37dc17e3 Thomas Thrainer
364 37dc17e3 Thomas Thrainer
    if self.op.add_reserved_ips:
365 37dc17e3 Thomas Thrainer
      for ip in self.op.add_reserved_ips:
366 37dc17e3 Thomas Thrainer
        try:
367 37dc17e3 Thomas Thrainer
          if self.pool.IsReserved(ip):
368 37dc17e3 Thomas Thrainer
            self.LogWarning("IP address %s is already reserved", ip)
369 37dc17e3 Thomas Thrainer
          else:
370 37dc17e3 Thomas Thrainer
            self.pool.Reserve(ip, external=True)
371 37dc17e3 Thomas Thrainer
        except errors.AddressPoolError, err:
372 37dc17e3 Thomas Thrainer
          self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
373 37dc17e3 Thomas Thrainer
374 37dc17e3 Thomas Thrainer
    if self.op.remove_reserved_ips:
375 37dc17e3 Thomas Thrainer
      for ip in self.op.remove_reserved_ips:
376 37dc17e3 Thomas Thrainer
        if ip == self.network.gateway:
377 37dc17e3 Thomas Thrainer
          self.LogWarning("Cannot unreserve Gateway's IP")
378 37dc17e3 Thomas Thrainer
          continue
379 37dc17e3 Thomas Thrainer
        try:
380 37dc17e3 Thomas Thrainer
          if not self.pool.IsReserved(ip):
381 37dc17e3 Thomas Thrainer
            self.LogWarning("IP address %s is already unreserved", ip)
382 37dc17e3 Thomas Thrainer
          else:
383 37dc17e3 Thomas Thrainer
            self.pool.Release(ip, external=True)
384 37dc17e3 Thomas Thrainer
        except errors.AddressPoolError, err:
385 37dc17e3 Thomas Thrainer
          self.LogWarning("Cannot release IP address %s: %s", ip, err)
386 37dc17e3 Thomas Thrainer
387 37dc17e3 Thomas Thrainer
    if self.op.mac_prefix:
388 37dc17e3 Thomas Thrainer
      self.network.mac_prefix = self.mac_prefix
389 37dc17e3 Thomas Thrainer
390 37dc17e3 Thomas Thrainer
    if self.op.network6:
391 37dc17e3 Thomas Thrainer
      self.network.network6 = self.network6
392 37dc17e3 Thomas Thrainer
393 37dc17e3 Thomas Thrainer
    if self.op.gateway6:
394 37dc17e3 Thomas Thrainer
      self.network.gateway6 = self.gateway6
395 37dc17e3 Thomas Thrainer
396 37dc17e3 Thomas Thrainer
    self.pool.Validate()
397 37dc17e3 Thomas Thrainer
398 37dc17e3 Thomas Thrainer
    self.cfg.Update(self.network, feedback_fn)
399 37dc17e3 Thomas Thrainer
400 37dc17e3 Thomas Thrainer
401 5eacbcae Thomas Thrainer
class NetworkQuery(QueryBase):
402 37dc17e3 Thomas Thrainer
  FIELDS = query.NETWORK_FIELDS
403 37dc17e3 Thomas Thrainer
404 37dc17e3 Thomas Thrainer
  def ExpandNames(self, lu):
405 44ffd981 Helga Velroyen
    raise NotImplementedError
406 37dc17e3 Thomas Thrainer
407 37dc17e3 Thomas Thrainer
  def DeclareLocks(self, lu, level):
408 44ffd981 Helga Velroyen
    raise NotImplementedError
409 37dc17e3 Thomas Thrainer
410 37dc17e3 Thomas Thrainer
  def _GetQueryData(self, lu):
411 44ffd981 Helga Velroyen
    raise NotImplementedError
412 37dc17e3 Thomas Thrainer
413 37dc17e3 Thomas Thrainer
  @staticmethod
414 37dc17e3 Thomas Thrainer
  def _GetStats(pool):
415 44ffd981 Helga Velroyen
    raise NotImplementedError
416 37dc17e3 Thomas Thrainer
417 37dc17e3 Thomas Thrainer
418 37dc17e3 Thomas Thrainer
class LUNetworkQuery(NoHooksLU):
419 37dc17e3 Thomas Thrainer
  """Logical unit for querying networks.
420 37dc17e3 Thomas Thrainer

421 37dc17e3 Thomas Thrainer
  """
422 37dc17e3 Thomas Thrainer
  REQ_BGL = False
423 37dc17e3 Thomas Thrainer
424 37dc17e3 Thomas Thrainer
  def CheckArguments(self):
425 44ffd981 Helga Velroyen
    raise NotImplementedError
426 37dc17e3 Thomas Thrainer
427 37dc17e3 Thomas Thrainer
  def ExpandNames(self):
428 44ffd981 Helga Velroyen
    raise NotImplementedError
429 37dc17e3 Thomas Thrainer
430 37dc17e3 Thomas Thrainer
  def Exec(self, feedback_fn):
431 44ffd981 Helga Velroyen
    raise NotImplementedError
432 37dc17e3 Thomas Thrainer
433 37dc17e3 Thomas Thrainer
434 37dc17e3 Thomas Thrainer
def _FmtNetworkConflict(details):
435 37dc17e3 Thomas Thrainer
  """Utility for L{_NetworkConflictCheck}.
436 37dc17e3 Thomas Thrainer

437 37dc17e3 Thomas Thrainer
  """
438 37dc17e3 Thomas Thrainer
  return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
439 37dc17e3 Thomas Thrainer
                         for (idx, ipaddr) in details)
440 37dc17e3 Thomas Thrainer
441 37dc17e3 Thomas Thrainer
442 37dc17e3 Thomas Thrainer
def _NetworkConflictCheck(lu, check_fn, action, instances):
443 37dc17e3 Thomas Thrainer
  """Checks for network interface conflicts with a network.
444 37dc17e3 Thomas Thrainer

445 37dc17e3 Thomas Thrainer
  @type lu: L{LogicalUnit}
446 37dc17e3 Thomas Thrainer
  @type check_fn: callable receiving one parameter (L{objects.NIC}) and
447 37dc17e3 Thomas Thrainer
    returning boolean
448 37dc17e3 Thomas Thrainer
  @param check_fn: Function checking for conflict
449 37dc17e3 Thomas Thrainer
  @type action: string
450 37dc17e3 Thomas Thrainer
  @param action: Part of error message (see code)
451 3fe41221 Klaus Aehlig
  @param instances: the instances to check
452 3fe41221 Klaus Aehlig
  @type instances: list of instance objects
453 37dc17e3 Thomas Thrainer
  @raise errors.OpPrereqError: If conflicting IP addresses are found.
454 37dc17e3 Thomas Thrainer

455 37dc17e3 Thomas Thrainer
  """
456 37dc17e3 Thomas Thrainer
  conflicts = []
457 37dc17e3 Thomas Thrainer
458 da4a52a3 Thomas Thrainer
  for instance in instances:
459 37dc17e3 Thomas Thrainer
    instconflicts = [(idx, nic.ip)
460 37dc17e3 Thomas Thrainer
                     for (idx, nic) in enumerate(instance.nics)
461 37dc17e3 Thomas Thrainer
                     if check_fn(nic)]
462 37dc17e3 Thomas Thrainer
463 37dc17e3 Thomas Thrainer
    if instconflicts:
464 37dc17e3 Thomas Thrainer
      conflicts.append((instance.name, instconflicts))
465 37dc17e3 Thomas Thrainer
466 37dc17e3 Thomas Thrainer
  if conflicts:
467 37dc17e3 Thomas Thrainer
    lu.LogWarning("IP addresses from network '%s', which is about to %s"
468 37dc17e3 Thomas Thrainer
                  " node group '%s', are in use: %s" %
469 37dc17e3 Thomas Thrainer
                  (lu.network_name, action, lu.group.name,
470 37dc17e3 Thomas Thrainer
                   utils.CommaJoin(("%s: %s" %
471 37dc17e3 Thomas Thrainer
                                    (name, _FmtNetworkConflict(details)))
472 37dc17e3 Thomas Thrainer
                                   for (name, details) in conflicts)))
473 37dc17e3 Thomas Thrainer
474 37dc17e3 Thomas Thrainer
    raise errors.OpPrereqError("Conflicting IP addresses found; "
475 37dc17e3 Thomas Thrainer
                               " remove/modify the corresponding network"
476 37dc17e3 Thomas Thrainer
                               " interfaces", errors.ECODE_STATE)
477 37dc17e3 Thomas Thrainer
478 37dc17e3 Thomas Thrainer
479 37dc17e3 Thomas Thrainer
class LUNetworkConnect(LogicalUnit):
480 37dc17e3 Thomas Thrainer
  """Connect a network to a nodegroup
481 37dc17e3 Thomas Thrainer

482 37dc17e3 Thomas Thrainer
  """
483 37dc17e3 Thomas Thrainer
  HPATH = "network-connect"
484 37dc17e3 Thomas Thrainer
  HTYPE = constants.HTYPE_NETWORK
485 37dc17e3 Thomas Thrainer
  REQ_BGL = False
486 37dc17e3 Thomas Thrainer
487 37dc17e3 Thomas Thrainer
  def ExpandNames(self):
488 37dc17e3 Thomas Thrainer
    self.network_name = self.op.network_name
489 37dc17e3 Thomas Thrainer
    self.group_name = self.op.group_name
490 37dc17e3 Thomas Thrainer
    self.network_mode = self.op.network_mode
491 37dc17e3 Thomas Thrainer
    self.network_link = self.op.network_link
492 37dc17e3 Thomas Thrainer
493 37dc17e3 Thomas Thrainer
    self.network_uuid = self.cfg.LookupNetwork(self.network_name)
494 37dc17e3 Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
495 37dc17e3 Thomas Thrainer
496 37dc17e3 Thomas Thrainer
    self.needed_locks = {
497 37dc17e3 Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
498 37dc17e3 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
499 37dc17e3 Thomas Thrainer
      }
500 37dc17e3 Thomas Thrainer
    self.share_locks[locking.LEVEL_INSTANCE] = 1
501 37dc17e3 Thomas Thrainer
502 37dc17e3 Thomas Thrainer
    if self.op.conflicts_check:
503 37dc17e3 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
504 37dc17e3 Thomas Thrainer
      self.share_locks[locking.LEVEL_NETWORK] = 1
505 37dc17e3 Thomas Thrainer
506 37dc17e3 Thomas Thrainer
  def DeclareLocks(self, level):
507 37dc17e3 Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
508 37dc17e3 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
509 37dc17e3 Thomas Thrainer
510 37dc17e3 Thomas Thrainer
      # Lock instances optimistically, needs verification once group lock has
511 37dc17e3 Thomas Thrainer
      # been acquired
512 37dc17e3 Thomas Thrainer
      if self.op.conflicts_check:
513 37dc17e3 Thomas Thrainer
        self.needed_locks[locking.LEVEL_INSTANCE] = \
514 da4a52a3 Thomas Thrainer
          self.cfg.GetInstanceNames(
515 da4a52a3 Thomas Thrainer
            self.cfg.GetNodeGroupInstances(self.group_uuid))
516 37dc17e3 Thomas Thrainer
517 37dc17e3 Thomas Thrainer
  def BuildHooksEnv(self):
518 37dc17e3 Thomas Thrainer
    ret = {
519 37dc17e3 Thomas Thrainer
      "GROUP_NAME": self.group_name,
520 37dc17e3 Thomas Thrainer
      "GROUP_NETWORK_MODE": self.network_mode,
521 37dc17e3 Thomas Thrainer
      "GROUP_NETWORK_LINK": self.network_link,
522 37dc17e3 Thomas Thrainer
      }
523 37dc17e3 Thomas Thrainer
    return ret
524 37dc17e3 Thomas Thrainer
525 37dc17e3 Thomas Thrainer
  def BuildHooksNodes(self):
526 1c3231aa Thomas Thrainer
    node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members
527 1c3231aa Thomas Thrainer
    return (node_uuids, node_uuids)
528 37dc17e3 Thomas Thrainer
529 37dc17e3 Thomas Thrainer
  def CheckPrereq(self):
530 37dc17e3 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
531 37dc17e3 Thomas Thrainer
532 37dc17e3 Thomas Thrainer
    assert self.group_uuid in owned_groups
533 37dc17e3 Thomas Thrainer
534 37dc17e3 Thomas Thrainer
    # Check if locked instances are still correct
535 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
536 37dc17e3 Thomas Thrainer
    if self.op.conflicts_check:
537 da4a52a3 Thomas Thrainer
      CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
538 37dc17e3 Thomas Thrainer
539 37dc17e3 Thomas Thrainer
    self.netparams = {
540 37dc17e3 Thomas Thrainer
      constants.NIC_MODE: self.network_mode,
541 37dc17e3 Thomas Thrainer
      constants.NIC_LINK: self.network_link,
542 37dc17e3 Thomas Thrainer
      }
543 37dc17e3 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(self.netparams)
544 37dc17e3 Thomas Thrainer
545 37dc17e3 Thomas Thrainer
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
546 37dc17e3 Thomas Thrainer
    #if self.network_mode == constants.NIC_MODE_BRIDGED:
547 37dc17e3 Thomas Thrainer
    #  _CheckNodeGroupBridgesExist(self, self.network_link, self.group_uuid)
548 37dc17e3 Thomas Thrainer
    self.connected = False
549 37dc17e3 Thomas Thrainer
    if self.network_uuid in self.group.networks:
550 37dc17e3 Thomas Thrainer
      self.LogWarning("Network '%s' is already mapped to group '%s'" %
551 37dc17e3 Thomas Thrainer
                      (self.network_name, self.group.name))
552 37dc17e3 Thomas Thrainer
      self.connected = True
553 37dc17e3 Thomas Thrainer
554 37dc17e3 Thomas Thrainer
    # check only if not already connected
555 37dc17e3 Thomas Thrainer
    elif self.op.conflicts_check:
556 37dc17e3 Thomas Thrainer
      pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
557 37dc17e3 Thomas Thrainer
558 da4a52a3 Thomas Thrainer
      _NetworkConflictCheck(
559 da4a52a3 Thomas Thrainer
        self, lambda nic: pool.Contains(nic.ip), "connect to",
560 3fe41221 Klaus Aehlig
        [instance_info for (_, instance_info) in
561 3fe41221 Klaus Aehlig
         self.cfg.GetMultiInstanceInfoByName(owned_instance_names)])
562 37dc17e3 Thomas Thrainer
563 37dc17e3 Thomas Thrainer
  def Exec(self, feedback_fn):
564 37dc17e3 Thomas Thrainer
    # Connect the network and update the group only if not already connected
565 37dc17e3 Thomas Thrainer
    if not self.connected:
566 37dc17e3 Thomas Thrainer
      self.group.networks[self.network_uuid] = self.netparams
567 37dc17e3 Thomas Thrainer
      self.cfg.Update(self.group, feedback_fn)
568 37dc17e3 Thomas Thrainer
569 37dc17e3 Thomas Thrainer
570 37dc17e3 Thomas Thrainer
class LUNetworkDisconnect(LogicalUnit):
571 37dc17e3 Thomas Thrainer
  """Disconnect a network to a nodegroup
572 37dc17e3 Thomas Thrainer

573 37dc17e3 Thomas Thrainer
  """
574 37dc17e3 Thomas Thrainer
  HPATH = "network-disconnect"
575 37dc17e3 Thomas Thrainer
  HTYPE = constants.HTYPE_NETWORK
576 37dc17e3 Thomas Thrainer
  REQ_BGL = False
577 37dc17e3 Thomas Thrainer
578 37dc17e3 Thomas Thrainer
  def ExpandNames(self):
579 37dc17e3 Thomas Thrainer
    self.network_name = self.op.network_name
580 37dc17e3 Thomas Thrainer
    self.group_name = self.op.group_name
581 37dc17e3 Thomas Thrainer
582 37dc17e3 Thomas Thrainer
    self.network_uuid = self.cfg.LookupNetwork(self.network_name)
583 37dc17e3 Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
584 37dc17e3 Thomas Thrainer
585 37dc17e3 Thomas Thrainer
    self.needed_locks = {
586 37dc17e3 Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
587 37dc17e3 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
588 37dc17e3 Thomas Thrainer
      }
589 37dc17e3 Thomas Thrainer
    self.share_locks[locking.LEVEL_INSTANCE] = 1
590 37dc17e3 Thomas Thrainer
591 37dc17e3 Thomas Thrainer
  def DeclareLocks(self, level):
592 37dc17e3 Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
593 37dc17e3 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
594 37dc17e3 Thomas Thrainer
595 37dc17e3 Thomas Thrainer
      # Lock instances optimistically, needs verification once group lock has
596 37dc17e3 Thomas Thrainer
      # been acquired
597 37dc17e3 Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
598 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNames(
599 da4a52a3 Thomas Thrainer
          self.cfg.GetNodeGroupInstances(self.group_uuid))
600 37dc17e3 Thomas Thrainer
601 37dc17e3 Thomas Thrainer
  def BuildHooksEnv(self):
602 37dc17e3 Thomas Thrainer
    ret = {
603 37dc17e3 Thomas Thrainer
      "GROUP_NAME": self.group_name,
604 37dc17e3 Thomas Thrainer
      }
605 37dc17e3 Thomas Thrainer
    return ret
606 37dc17e3 Thomas Thrainer
607 37dc17e3 Thomas Thrainer
  def BuildHooksNodes(self):
608 37dc17e3 Thomas Thrainer
    nodes = self.cfg.GetNodeGroup(self.group_uuid).members
609 37dc17e3 Thomas Thrainer
    return (nodes, nodes)
610 37dc17e3 Thomas Thrainer
611 37dc17e3 Thomas Thrainer
  def CheckPrereq(self):
612 37dc17e3 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
613 37dc17e3 Thomas Thrainer
614 37dc17e3 Thomas Thrainer
    assert self.group_uuid in owned_groups
615 37dc17e3 Thomas Thrainer
616 37dc17e3 Thomas Thrainer
    # Check if locked instances are still correct
617 37dc17e3 Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
618 5eacbcae Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
619 37dc17e3 Thomas Thrainer
620 37dc17e3 Thomas Thrainer
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
621 37dc17e3 Thomas Thrainer
    self.connected = True
622 37dc17e3 Thomas Thrainer
    if self.network_uuid not in self.group.networks:
623 37dc17e3 Thomas Thrainer
      self.LogWarning("Network '%s' is not mapped to group '%s'",
624 37dc17e3 Thomas Thrainer
                      self.network_name, self.group.name)
625 37dc17e3 Thomas Thrainer
      self.connected = False
626 37dc17e3 Thomas Thrainer
627 37dc17e3 Thomas Thrainer
    # We need this check only if network is not already connected
628 37dc17e3 Thomas Thrainer
    else:
629 da4a52a3 Thomas Thrainer
      _NetworkConflictCheck(
630 da4a52a3 Thomas Thrainer
        self, lambda nic: nic.network == self.network_uuid, "disconnect from",
631 32449822 Dimitris Aragiorgis
        [instance_info for (_, instance_info) in
632 e0e44476 Michele Tartara
         self.cfg.GetMultiInstanceInfoByName(owned_instances)])
633 37dc17e3 Thomas Thrainer
634 37dc17e3 Thomas Thrainer
  def Exec(self, feedback_fn):
635 37dc17e3 Thomas Thrainer
    # Disconnect the network and update the group only if network is connected
636 37dc17e3 Thomas Thrainer
    if self.connected:
637 37dc17e3 Thomas Thrainer
      del self.group.networks[self.network_uuid]
638 37dc17e3 Thomas Thrainer
      self.cfg.Update(self.group, feedback_fn)