def __prepare_key_pair(self, key_name, private_key_path, public_key_path, password):
        if not key_name:
            log.warn('user_key_name has not been defined, assuming password-based authentication')
            return

        if key_name in [k.name for k in self.driver.list_key_pairs()]:
            log.info('Key pair `%s` already exists, skipping import.', key_name)
            return

        if public_key_path:
            log.debug("importing public key from file %s ...", public_key_path)
            if not self.driver.import_key_pair_from_file(
                    name=key_name,
                    key_file_path=os.path.expandvars(os.path.expanduser(public_key_path))):
                raise KeypairError(
                    'Could not upload public key {p}'
                    .format(p=public_key_path))
        elif private_key_path:
            if not private_key_path.endswith('.pem'):
                raise KeypairError(
                    'can only work with .pem private keys,'
                    ' derive public key and set user_key_public')
            log.debug("deriving and importing public key from private key")
            self.__import_pem(key_name, private_key_path, password)
        else:
            pem_file_path = os.path.join(self.storage_path, key_name + '.pem')
            if not os.path.exists(pem_file_path):
                with open(pem_file_path, 'w') as new_key_file:
                    new_key_file.write(
                        self.driver.create_key_pair(name=key_name))
            self.__import_pem(key_name, pem_file_path, password)
    def __prepare_key_pair(self, key_name, private_key_path, public_key_path, password):
        if not key_name:
            log.warn('user_key_name has not been defined, assuming password-based authentication')
            return

        if key_name in [k.name for k in self.driver.list_key_pairs()]:
            log.info('Key pair `%s` already exists, skipping import.', key_name)
            return

        if public_key_path:
            log.debug("importing public key from file %s ...", public_key_path)
            if not self.driver.import_key_pair_from_file(
                    name=key_name,
                    key_file_path=os.path.expandvars(os.path.expanduser(public_key_path))):
                raise KeypairError(
                    'Could not upload public key {p}'
                    .format(p=public_key_path))
        elif private_key_path:
            if not private_key_path.endswith('.pem'):
                raise KeypairError(
                    'can only work with .pem private keys,'
                    ' derive public key and set user_key_public')
            log.debug("deriving and importing public key from private key")
            self.__import_pem(key_name, private_key_path, password)
        else:
            pem_file_path = os.path.join(self.storage_path, key_name + '.pem')
            if not os.path.exists(pem_file_path):
                with open(pem_file_path, 'w') as new_key_file:
                    new_key_file.write(
                        self.driver.create_key_pair(name=key_name))
            self.__import_pem(key_name, pem_file_path, password)
 def __get_instance(self, instance_id):
     for node in self.driver.list_nodes():
         if node.id == instance_id:
             return node
     else:
         log.warn('could not find instance with id %s', instance_id)
         return None
 def stop_instance(self, instance_id):
     instance = self.__get_instance(instance_id)
     if not instance:
         log.warn('could not find instance with id %s', instance_id)
         return
     log.info('stopping %s', instance.name)
     instance.destroy()
 def __get_instance(self, instance_id):
     for node in self.driver.list_nodes():
         if node.id == instance_id:
             return node
     else:
         log.warn('could not find instance with id %s', instance_id)
         return None
 def to_vars_dict(self):
     """
     Return local state which is relevant for the cluster setup process.
     """
     log.warn(
         "ElastiCluster's LibCloud backend is unable"
         " to export cloud connection information to the setup process."
         " Cloud access (e.g., auto-mounting of storage)"
         " will not be available from within the cluster.")
     return {}
Beispiel #7
0
 def _get_flavor_by_name(self, name):
     flavors = [
         flavor for flavor in self.driver.list_sizes()
         if (flavor.name == name or flavor.id == name)
     ]
     if flavors:
         flavor = flavors[0]
         if len(flavors) > 1:
             log.warn(
                 "%d flavors with name '%s' found!"
                 " using first returned one: %s", len(flavors), flavor)
         return flavor
     else:
         raise FlavorError("Cannot find flavor `%s`" % name)
 def _get_flavor_by_name(self, name):
     flavors = [
         flavor for flavor in self.driver.list_sizes()
         if (flavor.name == name or flavor.id == name)
     ]
     if flavors:
         flavor = flavors[0]
         if len(flavors) > 1:
             log.warn(
                 "%d flavors with name '%s' found!"
                 " using first returned one: %s",
                 len(flavors), flavor)
         return flavor
     else:
         raise FlavorError("Cannot find flavor `%s`" % name)
    def __get_function_by_pattern(self, pattern):
        """
        Return first function whose name *contains* the string `pattern`.

        :param func: partial function name (ex. key_pair)
        :return: list function that goes with it (ex. list_key_pairs)
        """
        function_names = [name for name in dir(self.driver) if pattern in name]
        if function_names:
            name = function_names[0]
            if len(function_names) > 1:
                log.warn(
                    "Several functions match pattern `%s`: %r -- using first one!",
                    pattern, function_names)
            return getattr(self.driver, name)
        else:
            # no such function
            raise AttributeError(
                "No function name contains `{0}` in class `{1}`".format(
                    pattern, self.__class__.__name__))
Beispiel #10
0
 def create_sfs_all(self):
     self.auth()
     if not self.is_create_sfs:
        if not self.query_all_sfs():
            log.error("can not find available sfs, please check your config")
            log.warn("your hwcc process will be killed!")
            os.system('kill -s 9 `pgrep hwcc`')
     else:
         self.auth()
         if not self.query_all_sfs():
             self.get_vpc_id()
             self.create_sfs()
             time.sleep(5)
             self.add_vpc_for_sfs()
             self.query_sfs_info()
         time.sleep(5)
         if not self.has_config():
             log.error("create sfs fail,kill the hwcc process")
             log.warn("your hwcc process will be killed!")
             os.system('kill -s 9 `pgrep hwcc`')
    def __prepare_key_pair(self, key_name, private_key_path, public_key_path, password):
        if not key_name:
            log.warn('user_key_name has not been defined, assuming password based authentication')
            return

        try:
            list_key_pairs = self.__get_function_by_pattern('list_key_pairs')
        except AttributeError:
            raise UnsupportedError('key management not supported by provider')
        try:
            self.__get_function_or_ex_function('import_key_pair_from_file')
        except AttributeError:
            raise UnsupportedError('key import not supported by provider')
        try:
            self.__get_function_or_ex_function('create_key_pair')
        except AttributeError:
            raise UnsupportedError('key creation not supported by provider')

        if key_name in [k.name for k in list_key_pairs()]:
            log.info('Key pair (%s) already exists, skipping import.', key_name)
            return

        if public_key_path:
            log.debug("importing public key from path %s", public_key_path)
            key_import = self.__get_function_or_ex_function('import_key_pair_from_file')
            if not key_import(name=key_name, key_file_path=os.path.expandvars(os.path.expanduser(public_key_path))):
                raise KeypairError('failure during import of public key {p}'.format(p=public_key_path))
        elif private_key_path:
            if not private_key_path.endswith('.pem'):
                raise KeypairError('can only work with .pem private keys, derive public key and set user_key_public')
            log.debug("deriving and importing public key from private key")
            self.__import_pem(key_name, private_key_path, password)
        elif os.path.exists(os.path.join(self.storage_path, '{p}.pem'.format(p=key_name))):
            self.__import_pem(key_name, os.path.join(self.storage_path, '{}.pem'.format(key_name)), password)
        else:
            with open(os.path.join(self.storage_path, '{p}.pem'.format(p=key_name)), 'w') as new_key_file:
                new_key_file.write(self.__get_function_or_ex_function('create_key_pair')(name=key_name))
            self.__import_pem(key_name, os.path.join(self.storage_path, '{p}.pem'.format(p=key_name)), password)
Beispiel #12
0
    def _allocate_address_neutron(self, instance, network_ids):
        """
        Allocates a floating/public ip address to the given instance,
        using the OpenStack Network ('Neutron') API.

        :param instance: instance to assign address to
        :param list network_id:
          List of IDs (as strings) of networks where to
          request allocation the floating IP.

        :return: public ip address
        """
        self._init_os_api()
        with OpenStackCloudProvider.__node_start_lock:
            # Note: to return *all* addresses, all parameters to
            # `neutron_client.list_floatingips()` should be left out;
            # setting them to `None` (e.g., `fixed_ip_address=None`)
            # results in an empty list...
            free_ips = [
                ip for ip in
                self.neutron_client.list_floatingips().get('floatingips')
                if (ip['floating_network_id'] in network_ids
                    # keep only unallocated IP addrs
                    and ip['fixed_ip_address'] is None
                    and ip['port_id'] is None)
            ]
            if free_ips:
                floating_ip = free_ips.pop()
                log.debug("Using existing floating IP %r", floating_ip)
            else:
                # FIXME: OpenStack Network API v2 requires that we specify
                # a network ID along with the request for a floating IP.
                # However, ElastiCluster configuration allows for multiple
                # networks to be connected to a VM, but does not give any
                # hint as to which one(s) should be used for such requests.
                # So we try them all, ignoring errors until one request
                # succeeds and hope that it's OK. One can imagine
                # scenarios where this is *not* correct, but: (1) these
                # scenarios are unlikely, and (2) the old novaclient code
                # above has not even had the concept of multiple networks
                # for floating IPs and no-one has complained in 5 years...
                for network_id in network_ids:
                    log.debug(
                        "Trying to allocate floating IP on network %s ...", network_id)
                    try:
                        floating_ip = self.neutron_client.create_floatingip({
                            'floatingip': {
                                'floating_network_id':network_id,
                            }}).get('floatingip')
                        log.debug(
                            "Allocated IP address %s on network %s",
                            floating_ip['floating_ip_address'], network_id)
                        break  # stop at first network where we get a floating IP
                    except BadNeutronRequest as err:
                        raise RuntimeError(
                            "Failed allocating floating IP on network {0}: {1}"
                            .format(network_id, err))
            if floating_ip.get('floating_ip_address', None) is None:
                raise RuntimeError(
                    "Could not allocate floating IP for VM {0}"
                    .format(instance_id))
            # wait until at least one interface is up
            interfaces = []
            # FIXMEE: no timeout!
            while not interfaces:
                interfaces = instance.interface_list()
                sleep(2)  ## FIXME: hard-coded value
            # get port ID
            for interface in interfaces:
                log.debug(
                    "Instance %s (ID: %s):"
                    " Checking if floating IP can be attached to interface %r ...",
                    instance.name, instance.id, interface)
                # if interface.net_id not in network_ids:
                #     log.debug(
                #         "Instance %s (ID: %s):"
                #         " Skipping interface %r:"
                #         " not attached to any of the requested networks.",
                #         instance.name, instance.id, interface)
                #     continue
                port_id = interface.port_id
                if port_id is None:
                    log.debug(
                        "Instance %s (ID: %s):"
                        " Skipping interface %r: no port ID!",
                        instance.name, instance.id, interface)
                    continue
                log.debug(
                    "Instance `%s` (ID: %s):"
                    " will assign floating IP to port ID %s (state: %s),"
                    " already running IP addresses %r",
                    instance.name, instance.id,
                    port_id, interface.port_state,
                    [item['ip_address'] for item in interface.fixed_ips])
                if interface.port_state != 'ACTIVE':
                    log.warn(
                        "Instance `%s` (ID: %s):"
                        " port `%s` is in state %s (epected 'ACTIVE' instead)",
                        instance.name, instance.id,
                        port_id, interface.port_state)
                break
            else:
                raise RuntimeError(
                    "Could not find port on network(s) {0}"
                    " for instance {1} (ID: {2}) to bind a floating IP to."
                    .format(network_ids, instance.name, instance.id))
            # assign floating IP to port
            floating_ip = self.neutron_client.update_floatingip(
                floating_ip['id'], {
                    'floatingip': {
                        'port_id': port_id,
                    },
                }
            ).get('floatingip')
            ip_address = floating_ip['floating_ip_address']
            log.debug("Assigned IP address %s to port %s", ip_address, port_id)

            log.info("Waiting 300s until floating IP %s is ACTIVE", ip_address)
            for i in range(300):
                _floating_ip = self.neutron_client.show_floatingip(floating_ip['id'])
                if _floating_ip['floatingip']['status'] != 'DOWN':
                    break
                sleep(1)

            # Invalidate cache for this VM, as we just assigned a new IP
            if instance.id in self._cached_instances:
                del self._cached_instances[instance.id]
        return ip_address
Beispiel #13
0
    def _allocate_address_neutron(self, instance, network_ids):
        """
        Allocates a floating/public ip address to the given instance,
        using the OpenStack Network ('Neutron') API.

        :param instance: instance to assign address to
        :param list network_id:
          List of IDs (as strings) of networks where to
          request allocation the floating IP.

        :return: public ip address
        """
        self._init_os_api()
        with OpenStackCloudProvider.__node_start_lock:
            # Note: to return *all* addresses, all parameters to
            # `neutron_client.list_floatingips()` should be left out;
            # setting them to `None` (e.g., `fixed_ip_address=None`)
            # results in an empty list...
            free_ips = [
                ip for ip in
                self.neutron_client.list_floatingips().get('floatingips')
                if (ip['floating_network_id'] in network_ids
                    # keep only unallocated IP addrs
                    and ip['fixed_ip_address'] is None
                    and ip['port_id'] is None)
            ]
            if free_ips:
                floating_ip = free_ips.pop()
                log.debug("Using existing floating IP %r", floating_ip)
            else:
                # FIXME: OpenStack Network API v2 requires that we specify
                # a network ID along with the request for a floating IP.
                # However, ElastiCluster configuration allows for multiple
                # networks to be connected to a VM, but does not give any
                # hint as to which one(s) should be used for such requests.
                # So we try them all, ignoring errors until one request
                # succeeds and hope that it's OK. One can imagine
                # scenarios where this is *not* correct, but: (1) these
                # scenarios are unlikely, and (2) the old novaclient code
                # above has not even had the concept of multiple networks
                # for floating IPs and no-one has complained in 5 years...
                for network_id in network_ids:
                    log.debug(
                        "Trying to allocate floating IP on network %s ...", network_id)
                    try:
                        floating_ip = self.neutron_client.create_floatingip({
                            'floatingip': {
                                'floating_network_id':network_id,
                            }}).get('floatingip')
                        log.debug(
                            "Allocated IP address %s on network %s",
                            floating_ip['floating_ip_address'], network_id)
                        break  # stop at first network where we get a floating IP
                    except BadNeutronRequest as err:
                        raise RuntimeError(
                            "Failed allocating floating IP on network {0}: {1}"
                            .format(network_id, err))
            if floating_ip.get('floating_ip_address', None) is None:
                raise RuntimeError(
                    "Could not allocate floating IP for VM {0}"
                    .format(instance_id))
            # wait until at least one interface is up
            interfaces = []
            # FIXMEE: no timeout!
            while not interfaces:
                interfaces = instance.interface_list()
                sleep(2)  ## FIXME: hard-coded value
            # get port ID
            for interface in interfaces:
                log.debug(
                    "Instance %s (ID: %s):"
                    " Checking if floating IP can be attached to interface %r ...",
                    instance.name, instance.id, interface)
                # if interface.net_id not in network_ids:
                #     log.debug(
                #         "Instance %s (ID: %s):"
                #         " Skipping interface %r:"
                #         " not attached to any of the requested networks.",
                #         instance.name, instance.id, interface)
                #     continue
                port_id = interface.port_id
                if port_id is None:
                    log.debug(
                        "Instance %s (ID: %s):"
                        " Skipping interface %r: no port ID!",
                        instance.name, instance.id, interface)
                    continue
                log.debug(
                    "Instance `%s` (ID: %s):"
                    " will assign floating IP to port ID %s (state: %s),"
                    " already running IP addresses %r",
                    instance.name, instance.id,
                    port_id, interface.port_state,
                    [item['ip_address'] for item in interface.fixed_ips])
                if interface.port_state != 'ACTIVE':
                    log.warn(
                        "Instance `%s` (ID: %s):"
                        " port `%s` is in state %s (epected 'ACTIVE' instead)",
                        instance.name, instance.id,
                        port_id, interface.port_state)
                break
            else:
                raise RuntimeError(
                    "Could not find port on network(s) {0}"
                    " for instance {1} (ID: {2}) to bind a floating IP to."
                    .format(network_ids, instance.name, instance.id))
            # assign floating IP to port
            floating_ip = self.neutron_client.update_floatingip(
                floating_ip['id'], {
                    'floatingip': {
                        'port_id': port_id,
                    },
                }
            ).get('floatingip')
            ip_address = floating_ip['floating_ip_address']
            log.debug("Assigned IP address %s to port %s", ip_address, port_id)

            log.info("Waiting 300s until floating IP %s is ACTIVE", ip_address)
            for i in range(300):
                _floating_ip = self.neutron_client.show_floatingip(floating_ip['id'])
                if _floating_ip['floatingip']['status'] != 'DOWN':
                    break
                sleep(1)

            # Invalidate cache for this VM, as we just assigned a new IP
            if instance.id in self._cached_instances:
                del self._cached_instances[instance.id]
        return ip_address
Beispiel #14
0
 def get_ips(self, instance_id):
     instance = self.__get_instance(instance_id)
     if not instance:
         log.warn('could not find instance with id %s', instance_id)
         return []
     return instance.public_ips + instance.private_ips
Beispiel #15
0
 def is_instance_running(self, instance_id):
     instance = self.__get_instance(instance_id)
     if not instance:
         log.warn('could not find instance with id %s', instance_id)
         return False
     return instance.state == NodeState.RUNNING
Beispiel #16
0
    def start_instance(self,
                       key_name,
                       public_key_path,
                       private_key_path,
                       security_group,
                       flavor,
                       image_id,
                       image_userdata,
                       availability_zone=None,
                       username=None,
                       node_name=None,
                       **kwargs):
        """Starts a new instance on the cloud using the given properties.
        The following tasks are done to start an instance:

        * establish a connection to the cloud web service
        * check ssh keypair and upload it if it does not yet exist. This is
          a locked process, since this function might be called in multiple
          threads and we only want the key to be stored once.
        * check if the security group exists
    * run the instance with the given properties

        :param str key_name: name of the ssh key to connect
        :param str public_key_path: path to ssh public key
        :param str private_key_path: path to ssh private key
        :param str security_group: firewall rule definition to apply on the
                                   instance
        :param str flavor: machine type to use for the instance
        :param str image_id: image type (os) to use for the instance
        :param str image_userdata: command to execute after startup
        :param str username: username for the given ssh key, default None

        :return: str - instance id of the started instance
        """
        self._init_os_api()

        vm_start_args = {}
        if 'availability_zone' in kwargs:
            availability_zone = kwargs.pop('availability_zone')
        vm_start_args['availability_zone'] = availability_zone
        log.debug("Checking keypair `%s` ...", key_name)
        with OpenStackCloudProvider.__node_start_lock:
            self._check_keypair(key_name, public_key_path, private_key_path)
        vm_start_args['key_name'] = key_name

        security_groups = [sg.strip() for sg in security_group.split(',')]
        self._check_security_groups(security_groups)
        vm_start_args['security_groups'] = security_groups

        # Check if the image id is present.
        if image_id not in [img.id for img in self._get_images()]:
            raise ImageError(
                "No image found with ID `{0}` in project `{1}` of cloud {2}".
                format(image_id, self._os_tenant_name, self._os_auth_url))
        vm_start_args['userdata'] = image_userdata

        # Check if the flavor exists
        flavors = [fl for fl in self._get_flavors() if fl.name == flavor]
        if not flavors:
            raise FlavorError(
                "No flavor found with name `{0}` in project `{1}` of cloud {2}"
                .format(flavor, self._os_tenant_name, self._os_auth_url))
        flavor = flavors[0]

        network_ids = [
            net_id.strip()
            for net_id in kwargs.pop('network_ids', '').split(',')
        ]
        if network_ids:
            nics = [{
                'net-id': net_id,
                'v4-fixed-ip': ''
            } for net_id in network_ids]
            log.debug("Specifying networks for node %s: %s", node_name,
                      ', '.join([nic['net-id'] for nic in nics]))
        else:
            nics = None
        vm_start_args['nics'] = nics

        if 'is_auto_renew' in kwargs:
            vm_start_args['is_auto_renew'] = kwargs['is_auto_renew']
        if 'is_auto_pay' in kwargs:
            vm_start_args['is_auto_pay'] = kwargs['is_auto_pay']
        if 'period_num' in kwargs:
            vm_start_args['period_num'] = int(kwargs['period_num'])
        if 'period_type' in kwargs:
            vm_start_args['period_type'] = kwargs['period_type']

        if 'boot_disk_size' in kwargs:
            # check if the backing volume is already there
            volume_name = '{name}-{id}'.format(name=node_name, id=image_id)
            if volume_name in [v.name for v in self._get_volumes()]:
                raise ImageError(
                    "Volume `{0}` already exists in project `{1}` of cloud {2}"
                    .format(volume_name, self._os_tenant_name,
                            self._os_auth_url))

            log.info('Creating volume `%s` to use as VM disk ...', volume_name)
            try:
                bds = int(kwargs['boot_disk_size'])
                if bds < 1:
                    raise ValueError('non-positive int')
            except (ValueError, TypeError):
                raise ConfigurationError(
                    "Invalid `boot_disk_size` specified:"
                    " should be a positive integer, got {0} instead".format(
                        kwargs['boot_disk_size']))
            volume = self.cinder_client.volumes.create(
                size=bds,
                name=volume_name,
                imageRef=image_id,
                volume_type=kwargs.pop('boot_disk_type'))
            # wait for volume to come up
            volume_available = False
            while not volume_available:
                for v in self._get_volumes():
                    if v.name == volume_name and v.status == 'available':
                        volume_available = True
                        break
                sleep(1)  # FIXME: hard-coded waiting time

            # ok, use volume as VM disk
            vm_start_args['block_device_mapping'] = {
                # FIXME: is it possible that `vda` is not the boot disk? e.g. if
                # a non-paravirtualized kernel is being used?  should we allow
                # to set the boot device as an image parameter?
                'vda':
                ('{id}:::{delete_on_terminate}'.format(id=volume.id,
                                                       delete_on_terminate=1)),
            }

        # due to some `nova_client.servers.create()` implementation weirdness,
    # the first three args nee  # conflated into `**vm_start_args`
        vm = None
        vm_instance_id = None
        if 'charging_mode' not in kwargs or kwargs[
                'charging_mode'] == "postPaid":
            vm = self.nova_client.servers.create(node_name, image_id, flavor,
                                                 **vm_start_args)
        else:
            order_info = self.create_prePaid_Server(node_name, image_id,
                                                    flavor.id.encode('utf-8'),
                                                    **vm_start_args)
            order_id = order_info.order_id
            is_paid_order = raw_input(
                "Please pay for your order %s , Enter [y/yes] if you have paid successfully! : "
                % order_id)
            if is_paid_order == "y" or is_paid_order == "yes":
                vm_instance_id = None
                while vm_instance_id is None:
                    vm_instance_id = self.query_prePaidRes(order_id)
                    time.sleep(5)
            else:
                is_confirm = raw_input(
                    "Warning : your input is not y or yes ,  confirm or not [y/n]: "
                )
                while is_confirm != "y" and is_confirm != "n":
                    is_confirm = raw_input("Please enter y or n: ")
                if is_confirm == 'y' or is_confirm == 'yes':
                    log.error('Payment Fail ')
                    log.warn("your hwcc process will be killed!")
                    os.system('kill -s 9 `pgrep hwcc`')
                else:
                    vm_instance_id = None
                    while vm_instance_id is None:
                        vm_instance_id = self.query_prePaidRes(order_id)
                        time.sleep(10)
            OpenStackCloudProvider.prePaid_id = vm_instance_id
            vm = VmInfo()

    #  vm = self.nova_client.servers.create(node_name,image_id, flavor, **vm_start_args)

    # allocate and attach a floating IP, if requested
        if self.request_floating_ip:
            # We need to list the floating IPs for this instance
            try:
                # python-novaclient <8.0.0
                floating_ips = [
                    ip for ip in self.nova_client.floating_ips.list()
                    if ip.instance_id == vm.id
                ]
            except AttributeError:
                floating_ips = self.neutron_client.list_floatingips(id=vm.id)
            # allocate new floating IP if none given
            if not floating_ips:
                self._allocate_address(vm, network_ids)

        self._instances[vm.id] = vm

        return vm.id