text: #
  #
  
  # Copyright (C) 2013 Google Inc.
  # All rights reserved.
  #
  # Redistribution and use in source and binary forms, with or without
  # modification, are permitted provided that the following conditions are
  # met:
  #
  # 1. Redistributions of source code must retain the above copyright notice,
  # this list of conditions and the following disclaimer.
  #
  # 2. Redistributions in binary form must reproduce the above copyright
  # notice, this list of conditions and the following disclaimer in the
  # documentation and/or other materials provided with the distribution.
  #
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  
  """Support for mocking the cluster configuration"""
  
  
  import random
  import time
  import uuid as uuid_module
  
  from ganeti import config
  from ganeti import constants
  from ganeti import errors
  from ganeti.network import AddressPool
  from ganeti import objects
  from ganeti import utils
  
  import mocks
  
  
  RESERVE_ACTION = "reserve"
  RELEASE_ACTION = "release"
  
  
  def _StubGetEntResolver():
    return mocks.FakeGetentResolver()
  
  
  # pylint: disable=R0904
  class ConfigMock(config.ConfigWriter):
    """A mocked cluster configuration with added methods for easy customization.
  
    """
  
    def __init__(self, cfg_file="/dev/null"):
      self._cur_group_id = 1
      self._cur_node_id = 1
      self._cur_inst_id = 1
      self._cur_disk_id = 1
      self._cur_os_id = 1
      self._cur_nic_id = 1
      self._cur_net_id = 1
      self._default_os = None
      self._mocked_config_store = None
  
      self._temporary_macs = config.TemporaryReservationManager()
      self._temporary_secrets = config.TemporaryReservationManager()
      self._temporary_lvs = config.TemporaryReservationManager()
      self._temporary_ips = config.TemporaryReservationManager()
  
      super(ConfigMock, self).__init__(cfg_file=cfg_file,
                                       _getents=_StubGetEntResolver(),
                                       offline=True)
  
      with self.GetConfigManager():
        self._CreateConfig()
  
    def _GetUuid(self):
      return str(uuid_module.uuid4())
  
    def _GetObjUuid(self, obj):
      if obj is None:
        return None
      elif isinstance(obj, objects.ConfigObject):
        return obj.uuid
      else:
        return obj
  
    def AddNewNodeGroup(self,
                        uuid=None,
                        name=None,
                        ndparams=None,
                        diskparams=None,
                        ipolicy=None,
                        hv_state_static=None,
                        disk_state_static=None,
                        alloc_policy=None,
                        networks=None):
      """Add a new L{objects.NodeGroup} to the cluster configuration
  
      See L{objects.NodeGroup} for parameter documentation.
  
      @rtype: L{objects.NodeGroup}
      @return: the newly added node group
  
      """
      group_id = self._cur_group_id
      self._cur_group_id += 1
  
      if uuid is None:
        uuid = self._GetUuid()
      if name is None:
        name = "mock_group_%d" % group_id
      if networks is None:
        networks = {}
  
      group = objects.NodeGroup(uuid=uuid,
                                name=name,
                                ndparams=ndparams,
                                diskparams=diskparams,
                                ipolicy=ipolicy,
                                hv_state_static=hv_state_static,
                                disk_state_static=disk_state_static,
                                alloc_policy=alloc_policy,
                                networks=networks,
                                members=[])
  
      self._UnlockedAddNodeGroup(group, None, True)
      return group
  
    # pylint: disable=R0913
    def AddNewNode(self,
                   uuid=None,
                   name=None,
                   primary_ip=None,
                   secondary_ip=None,
                   master_candidate=True,
                   offline=False,
                   drained=False,
                   group=None,
                   master_capable=True,
                   vm_capable=True,
                   ndparams=None,
                   powered=True,
                   hv_state=None,
                   hv_state_static=None,
                   disk_state=None,
                   disk_state_static=None):
      """Add a new L{objects.Node} to the cluster configuration
  
      See L{objects.Node} for parameter documentation.
  
      @rtype: L{objects.Node}
      @return: the newly added node
  
      """
      node_id = self._cur_node_id
      self._cur_node_id += 1
  
      if uuid is None:
        uuid = self._GetUuid()
      if name is None:
        name = "mock_node_%d.example.com" % node_id
      if primary_ip is None:
        primary_ip = "192.0.2.%d" % node_id
      if secondary_ip is None:
        secondary_ip = "203.0.113.%d" % node_id
      if group is None:
        group = self._default_group.uuid
      group = self._GetObjUuid(group)
      if ndparams is None:
        ndparams = {}
  
      node = objects.Node(uuid=uuid,
                          name=name,
                          primary_ip=primary_ip,
                          secondary_ip=secondary_ip,
                          master_candidate=master_candidate,
                          offline=offline,
                          drained=drained,
                          group=group,
                          master_capable=master_capable,
                          vm_capable=vm_capable,
                          ndparams=ndparams,
                          powered=powered,
                          hv_state=hv_state,
                          hv_state_static=hv_state_static,
                          disk_state=disk_state,
                          disk_state_static=disk_state_static)
  
      self._UnlockedAddNode(node, None)
      return node
  
    def AddNewInstance(self,
                       uuid=None,
                       name=None,
                       primary_node=None,
                       os=None,
                       hypervisor=None,
                       hvparams=None,
                       beparams=None,
                       osparams=None,
                       osparams_private=None,
                       admin_state=None,
                       admin_state_source=None,
                       nics=None,
                       disks=None,
                       disk_template=None,
                       disks_active=None,
                       network_port=None,
                       secondary_node=None):
      """Add a new L{objects.Instance} to the cluster configuration
  
      See L{objects.Instance} for parameter documentation.
  
      @rtype: L{objects.Instance}
      @return: the newly added instance
  
      """
      inst_id = self._cur_inst_id
      self._cur_inst_id += 1
  
      if uuid is None:
        uuid = self._GetUuid()
      if name is None:
        name = "mock_inst_%d.example.com" % inst_id
      if primary_node is None:
        primary_node = self._master_node.uuid
      primary_node = self._GetObjUuid(primary_node)
      if os is None:
        os = self.GetDefaultOs().name + objects.OS.VARIANT_DELIM +\
             self.GetDefaultOs().supported_variants[0]
      if hypervisor is None:
        hypervisor = self.GetClusterInfo().enabled_hypervisors[0]
      if hvparams is None:
        hvparams = {}
      if beparams is None:
        beparams = {}
      if osparams is None:
        osparams = {}
      if osparams_private is None:
        osparams_private = {}
      if admin_state is None:
        admin_state = constants.ADMINST_DOWN
      if admin_state_source is None:
        admin_state_source = constants.ADMIN_SOURCE
      if nics is None:
        nics = [self.CreateNic()]
      if disk_template is None:
        if disks is None:
          # user chose nothing, so create a plain disk for him
          disk_template = constants.DT_PLAIN
        elif len(disks) == 0:
          disk_template = constants.DT_DISKLESS
        else:
          disk_template = disks[0].dev_type
      if disks is None:
        if disk_template == constants.DT_DISKLESS:
          disks = []
        else:
          disks = [self.CreateDisk(dev_type=disk_template,
                                   primary_node=primary_node,
                                   secondary_node=secondary_node)]
      if disks_active is None:
        disks_active = admin_state == constants.ADMINST_UP
  
      inst = objects.Instance(uuid=uuid,
                              name=name,
                              primary_node=primary_node,
                              os=os,
                              hypervisor=hypervisor,
                              hvparams=hvparams,
                              beparams=beparams,
                              osparams=osparams,
                              osparams_private=osparams_private,
                              admin_state=admin_state,
                              admin_state_source=admin_state_source,
                              nics=nics,
                              disks=[],
                              disk_template=disk_template,
                              disks_active=disks_active,
                              network_port=network_port)
      self.AddInstance(inst, None)
      for disk in disks:
        self.AddInstanceDisk(inst.uuid, disk)
      return inst
  
    def AddNewNetwork(self,
                      uuid=None,
                      name=None,
                      mac_prefix=None,
                      network=None,
                      network6=None,
                      gateway=None,
                      gateway6=None,
                      reservations=None,
                      ext_reservations=None):
      """Add a new L{objects.Network} to the cluster configuration
  
      See L{objects.Network} for parameter documentation.
  
      @rtype: L{objects.Network}
      @return: the newly added network
  
      """
      net_id = self._cur_net_id
      self._cur_net_id += 1
  
      if uuid is None:
        uuid = self._GetUuid()
      if name is None:
        name = "mock_net_%d" % net_id
      if network is None:
        network = "198.51.100.0/24"
      if gateway is None:
        if network[-3:] == "/24":
          gateway = network[:-4] + "1"
        else:
          gateway = "198.51.100.1"
      if network[-3:] == "/24" and gateway == network[:-4] + "1":
        if reservations is None:
          reservations = "0" * 256
        if ext_reservations:
          ext_reservations = "11" + ("0" * 253) + "1"
      elif reservations is None or ext_reservations is None:
        raise AssertionError("You have to specify 'reservations' and"
                             " 'ext_reservations'!")
  
      net = objects.Network(uuid=uuid,
                            name=name,
                            mac_prefix=mac_prefix,
                            network=network,
                            network6=network6,
                            gateway=gateway,
                            gateway6=gateway6,
                            reservations=reservations,
                            ext_reservations=ext_reservations)
      self.AddNetwork(net, None)
      return net
  
    def ConnectNetworkToGroup(self, net, group, netparams=None):
      """Connect the given network to the group.
  
      @type net: string or L{objects.Network}
      @param net: network object or UUID
      @type group: string of L{objects.NodeGroup}
      @param group: node group object of UUID
      @type netparams: dict
      @param netparams: network parameters for this connection
  
      """
      net_obj = None
      if isinstance(net, objects.Network):
        net_obj = net
      else:
        net_obj = self.GetNetwork(net)
  
      group_obj = None
      if isinstance(group, objects.NodeGroup):
        group_obj = group
      else:
        group_obj = self.GetNodeGroup(group)
  
      if net_obj is None or group_obj is None:
        raise AssertionError("Failed to get network or node group")
  
      if netparams is None:
        netparams = {
          constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
          constants.NIC_LINK: "br_mock"
        }
  
      group_obj.networks[net_obj.uuid] = netparams
  
    def CreateDisk(self,
                   uuid=None,
                   name=None,
                   dev_type=constants.DT_PLAIN,
                   logical_id=None,
                   children=None,
                   iv_name=None,
                   size=1024,
                   mode=constants.DISK_RDWR,
                   params=None,
                   spindles=None,
                   primary_node=None,
                   secondary_node=None,
                   create_nodes=False,
                   instance_disk_index=0):
      """Create a new L{objecs.Disk} object
  
      @rtype: L{objects.Disk}
      @return: the newly create disk object
  
      """
      disk_id = self._cur_disk_id
      self._cur_disk_id += 1
  
      if uuid is None:
        uuid = self._GetUuid()
      if name is None:
        name = "mock_disk_%d" % disk_id
  
      if dev_type == constants.DT_DRBD8:
        pnode_uuid = self._GetObjUuid(primary_node)
        snode_uuid = self._GetObjUuid(secondary_node)
        if logical_id is not None:
          pnode_uuid = logical_id[0]
          snode_uuid = logical_id[1]
  
        if pnode_uuid is None and create_nodes:
          pnode_uuid = self.AddNewNode().uuid
        if snode_uuid is None and create_nodes:
          snode_uuid = self.AddNewNode().uuid
  
        if pnode_uuid is None or snode_uuid is None:
          raise AssertionError("Trying to create DRBD disk without nodes!")
  
        if logical_id is None:
          logical_id = (pnode_uuid, snode_uuid,
                        constants.FIRST_DRBD_PORT + disk_id,
                        disk_id, disk_id, "mock_secret")
        if children is None:
          data_child = self.CreateDisk(dev_type=constants.DT_PLAIN,
                                       size=size)
          meta_child = self.CreateDisk(dev_type=constants.DT_PLAIN,
                                       size=constants.DRBD_META_SIZE)
          children = [data_child, meta_child]
      elif dev_type == constants.DT_PLAIN:
        if logical_id is None:
          logical_id = ("mockvg", "mock_disk_%d" % disk_id)
      elif dev_type in constants.DTS_FILEBASED:
        if logical_id is None:
          logical_id = (constants.FD_LOOP, "/file/storage/disk%d" % disk_id)
      elif dev_type == constants.DT_BLOCK:
        if logical_id is None:
          logical_id = (constants.BLOCKDEV_DRIVER_MANUAL,
                        "/dev/disk/disk%d" % disk_id)
      elif logical_id is None:
        raise NotImplementedError
      if children is None:
        children = []
      if iv_name is None:
        iv_name = "disk/%d" % instance_disk_index
      if params is None:
        params = {}
  
      return objects.Disk(uuid=uuid,
                          name=name,
                          dev_type=dev_type,
                          logical_id=logical_id,
                          children=children,
                          iv_name=iv_name,
                          size=size,
                          mode=mode,
                          params=params,
                          spindles=spindles)
  
    def GetDefaultOs(self):
      if self._default_os is None:
        self._default_os = self.CreateOs(name="mocked_os")
      return self._default_os
  
    def CreateOs(self,
                 name=None,
                 path=None,
                 api_versions=None,
                 create_script=None,
                 export_script=None,
                 import_script=None,
                 rename_script=None,
                 verify_script=None,
                 supported_variants=None,
                 supported_parameters=None):
      """Create a new L{objects.OS} object
  
      @rtype: L{object.OS}
      @return: the newly create OS objects
  
      """
      os_id = self._cur_os_id
      self._cur_os_id += 1
  
      if name is None:
        name = "mock_os_%d" % os_id
      if path is None:
        path = "/mocked/path/%d" % os_id
      if api_versions is None:
        api_versions = [constants.OS_API_V20]
      if create_script is None:
        create_script = "mock_create.sh"
      if export_script is None:
        export_script = "mock_export.sh"
      if import_script is None:
        import_script = "mock_import.sh"
      if rename_script is None:
        rename_script = "mock_rename.sh"
      if verify_script is None:
        verify_script = "mock_verify.sh"
      if supported_variants is None:
        supported_variants = ["default"]
      if supported_parameters is None:
        supported_parameters = ["mock_param"]
  
      return objects.OS(name=name,
                        path=path,
                        api_versions=api_versions,
                        create_script=create_script,
                        export_script=export_script,
                        import_script=import_script,
                        rename_script=rename_script,
                        verify_script=verify_script,
                        supported_variants=supported_variants,
                        supported_parameters=supported_parameters)
  
    def CreateNic(self,
                  uuid=None,
                  name=None,
                  mac=None,
                  ip=None,
                  network=None,
                  nicparams=None,
                  netinfo=None):
      """Create a new L{objecs.NIC} object
  
      @rtype: L{objects.NIC}
      @return: the newly create NIC object
  
      """
      nic_id = self._cur_nic_id
      self._cur_nic_id += 1
  
      if uuid is None:
        uuid = self._GetUuid()
      if name is None:
        name = "mock_nic_%d" % nic_id
      if mac is None:
        mac = "aa:00:00:aa:%02x:%02x" % (nic_id / 0xff, nic_id % 0xff)
      if isinstance(network, objects.Network):
        if ip:
          pool = AddressPool(network)
          pool.Reserve(ip)
        network = network.uuid
      if nicparams is None:
        nicparams = {}
  
      return objects.NIC(uuid=uuid,
                         name=name,
                         mac=mac,
                         ip=ip,
                         network=network,
                         nicparams=nicparams,
                         netinfo=netinfo)
  
    def SetEnabledDiskTemplates(self, enabled_disk_templates):
      """Set the enabled disk templates in the cluster.
  
      This also takes care of required IPolicy updates.
  
      @type enabled_disk_templates: list of string
      @param enabled_disk_templates: list of disk templates to enable
  
      """
      cluster = self.GetClusterInfo()
      cluster.enabled_disk_templates = list(enabled_disk_templates)
      cluster.ipolicy[constants.IPOLICY_DTS] = list(enabled_disk_templates)
  
    def ComputeDRBDMap(self):
      return dict((node_uuid, {}) for node_uuid in self._ConfigData().nodes)
  
    def AllocateDRBDMinor(self, node_uuids, inst_uuid):
      return map(lambda _: 0, node_uuids)
  
    def _UnlockedReleaseDRBDMinors(self, inst_uuid):
      pass
  
    def SetIPolicyField(self, category, field, value):
      """Set a value of a desired ipolicy field.
  
      @type category: one of L{constants.ISPECS_MAX}, L{constants.ISPECS_MIN},
        L{constants.ISPECS_STD}
      @param category: Whether to change the default value, or the upper or lower
        bound.
      @type field: string
      @param field: The field to change.
      @type value: any
      @param value: The value to assign.
  
      """
      if category not in [constants.ISPECS_MAX, constants.ISPECS_MIN,
                          constants.ISPECS_STD]:
        raise ValueError("Invalid ipolicy category %s" % category)
  
      ipolicy_dict = self.GetClusterInfo().ipolicy[constants.ISPECS_MINMAX][0]
      ipolicy_dict[category][field] = value
  
    def _CreateConfig(self):
      self._config_data = objects.ConfigData(
        version=constants.CONFIG_VERSION,
        cluster=None,
        nodegroups={},
        nodes={},
        instances={},
        networks={},
        disks={})
  
      master_node_uuid = self._GetUuid()
  
      self._cluster = objects.Cluster(
        serial_no=1,
        rsahostkeypub="",
        highest_used_port=(constants.FIRST_DRBD_PORT - 1),
        tcpudp_port_pool=set(),
        mac_prefix="aa:00:00",
        volume_group_name="xenvg",
        reserved_lvs=None,
        drbd_usermode_helper="/bin/true",
        master_node=master_node_uuid,
        master_ip="192.0.2.254",
        master_netdev=constants.DEFAULT_BRIDGE,
        master_netmask=None,
        use_external_mip_script=None,
        cluster_name="cluster.example.com",
        file_storage_dir="/tmp",
        shared_file_storage_dir=None,
        enabled_hypervisors=[constants.HT_XEN_HVM, constants.HT_XEN_PVM,
                             constants.HT_KVM],
        hvparams=constants.HVC_DEFAULTS.copy(),
        ipolicy=None,
        os_hvp={self.GetDefaultOs().name: constants.HVC_DEFAULTS.copy()},
        beparams=None,
        osparams=None,
        osparams_private_cluster=None,
        nicparams={constants.PP_DEFAULT: constants.NICC_DEFAULTS},
        ndparams=None,
        diskparams=None,
        candidate_pool_size=3,
        modify_etc_hosts=False,
        modify_ssh_setup=False,
        maintain_node_health=False,
        uid_pool=None,
        default_iallocator="mock_iallocator",
        hidden_os=None,
        blacklisted_os=None,
        primary_ip_family=None,
        prealloc_wipe_disks=None,
        enabled_disk_templates=list(constants.DISK_TEMPLATE_PREFERENCE),
        )
      self._cluster.ctime = self._cluster.mtime = time.time()
      self._cluster.UpgradeConfig()
      self._ConfigData().cluster = self._cluster
  
      self._default_group = self.AddNewNodeGroup(name="default")
      self._master_node = self.AddNewNode(uuid=master_node_uuid)
  
    def _OpenConfig(self, _accept_foreign):
      self._config_data = self._mocked_config_store
  
    def _WriteConfig(self, destination=None):
      self._mocked_config_store = self._ConfigData()
  
    def _GetRpc(self, _address_list):
      raise AssertionError("This should not be used during tests!")
  
    def _UnlockedGetNetworkMACPrefix(self, net_uuid):
      """Return the network mac prefix if it exists or the cluster level default.
  
      """
      prefix = None
      if net_uuid:
        nobj = self._UnlockedGetNetwork(net_uuid)
        if nobj.mac_prefix:
          prefix = nobj.mac_prefix
  
      return prefix
  
    def _GenerateOneMAC(self, prefix=None):
      """Return a function that randomly generates a MAC suffic
         and appends it to the given prefix. If prefix is not given get
         the cluster level default.
  
      """
      if not prefix:
        prefix = self._ConfigData().cluster.mac_prefix
  
      def GenMac():
        byte1 = random.randrange(0, 256)
        byte2 = random.randrange(0, 256)
        byte3 = random.randrange(0, 256)
        mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
        return mac
  
      return GenMac
  
    def GenerateMAC(self, net_uuid, ec_id):
      """Generate a MAC for an instance.
  
      This should check the current instances for duplicates.
  
      """
      existing = self._AllMACs()
      prefix = self._UnlockedGetNetworkMACPrefix(net_uuid)
      gen_mac = self._GenerateOneMAC(prefix)
      return self._temporary_macs.Generate(existing, gen_mac, ec_id)
  
    def ReserveMAC(self, mac, ec_id):
      """Reserve a MAC for an instance.
  
      This only checks instances managed by this cluster, it does not
      check for potential collisions elsewhere.
  
      """
      all_macs = self._AllMACs()
      if mac in all_macs:
        raise errors.ReservationError("mac already in use")
      else:
        self._temporary_macs.Reserve(ec_id, mac)
  
    def GenerateDRBDSecret(self, ec_id):
      """Generate a DRBD secret.
  
      This checks the current disks for duplicates.
  
      """
      return self._temporary_secrets.Generate(self._AllDRBDSecrets(),
                                              utils.GenerateSecret,
                                              ec_id)
  
    def ReserveLV(self, lv_name, ec_id):
      """Reserve an VG/LV pair for an instance.
  
      @type lv_name: string
      @param lv_name: the logical volume name to reserve
  
      """
      all_lvs = self._AllLVs()
      if lv_name in all_lvs:
        raise errors.ReservationError("LV already in use")
      else:
        self._temporary_lvs.Reserve(ec_id, lv_name)
  
    def _UnlockedCommitTemporaryIps(self, ec_id):
      """Commit all reserved IP address to their respective pools
  
      """
      for action, address, net_uuid in self._temporary_ips.GetECReserved(ec_id):
        self._UnlockedCommitIp(action, net_uuid, address)
  
    def _UnlockedCommitIp(self, action, net_uuid, address):
      """Commit a reserved IP address to an IP pool.
  
      The IP address is taken from the network's IP pool and marked as reserved.
  
      """
      nobj = self._UnlockedGetNetwork(net_uuid)
      pool = AddressPool(nobj)
      if action == RESERVE_ACTION:
        pool.Reserve(address)
      elif action == RELEASE_ACTION:
        pool.Release(address)
  
    def _UnlockedReleaseIp(self, net_uuid, address, ec_id):
      """Give a specific IP address back to an IP pool.
  
      The IP address is returned to the IP pool designated by pool_id and marked
      as reserved.
  
      """
      self._temporary_ips.Reserve(ec_id,
                                  (RELEASE_ACTION, address, net_uuid))
  
    def ReleaseIp(self, net_uuid, address, ec_id):
      """Give a specified IP address back to an IP pool.
  
      This is just a wrapper around _UnlockedReleaseIp.
  
      """
      if net_uuid:
        self._UnlockedReleaseIp(net_uuid, address, ec_id)
  
    def GenerateIp(self, net_uuid, ec_id):
      """Find a free IPv4 address for an instance.
  
      """
      nobj = self._UnlockedGetNetwork(net_uuid)
      pool = AddressPool(nobj)
  
      def gen_one():
        try:
          ip = pool.GenerateFree()
        except errors.AddressPoolError:
          raise errors.ReservationError("Cannot generate IP. Network is full")
        return (RESERVE_ACTION, ip, net_uuid)
  
      _, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
      return address
  
    def _UnlockedReserveIp(self, net_uuid, address, ec_id, check=True):
      """Reserve a given IPv4 address for use by an instance.
  
      """
      nobj = self._UnlockedGetNetwork(net_uuid)
      pool = AddressPool(nobj)
      try:
        isreserved = pool.IsReserved(address)
        isextreserved = pool.IsReserved(address, external=True)
      except errors.AddressPoolError:
        raise errors.ReservationError("IP address not in network")
      if isreserved:
        raise errors.ReservationError("IP address already in use")
      if check and isextreserved:
        raise errors.ReservationError("IP is externally reserved")
      return self._temporary_ips.Reserve(ec_id,
                                         (RESERVE_ACTION,
                                          address, net_uuid))
  
    def ReserveIp(self, net_uuid, address, ec_id, check=True):
      """Reserve a given IPv4 address for use by an instance.
  
      """
      if net_uuid:
        return self._UnlockedReserveIp(net_uuid, address, ec_id, check)
  