def start_instance(self, key_name, public_key_path, private_key_path, security_group, flavor, image_id, image_userdata, 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 """ log.debug("Checking keypair `%s`.", key_name) with OpenStackCloudProvider.__node_start_lock: self._check_keypair(key_name, public_key_path, private_key_path) log.debug("Checking security group `%s`.", security_group) self._check_security_group(security_group) # Check if the image id is present. images = self._get_images() if image_id not in [img.id for img in images]: raise ImageError("No image found with id '%s' on cloud " "%s" % (image_id, self._os_auth_url)) # 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 %s on cloud " "%s" % (flavor, self._os_auth_url)) flavor = flavors[0] nics = None if 'network_ids' in kwargs: nics=[{'net-id': netid.strip(), 'v4-fixed-ip': ''} for netid in kwargs['network_ids'].split(',') ] log.debug("Specifying networks for vm %s: %s", node_name, str.join(', ', [nic['net-id'] for nic in nics])) vm = self.client.servers.create( node_name, image_id, flavor, key_name=key_name, security_groups=[security_group], userdata=image_userdata, nics=nics) self._instances[vm.id] = vm return vm.id
def _find_image_id(self, image_id): """ Finds an image id to a given id or name. """ if not self._images: connection = self._connect() self._images = connection.get_all_images() image_id_cloud = None for i in self._images: if i.id == image_id or i.name == image_id: image_id_cloud = i.id break if image_id_cloud: return image_id_cloud else: raise ImageError( "Could not find given image id `%s`" % image_id)
def _get_image_url(self, image_id): """Gets the url for the specified image. Unfortunatly this only works for images uploaded by the user. The images provided by google will not be found. :param str image_id: image identifier :return: str - api url of the image """ gce = self._connect() filter = "name eq %s" % image_id request = gce.images().list(project=self._project_id, filter=filter) response = self._execute_request(request) response = self._wait_until_done(response) image_url = None if "items" in response: image_url = response["items"][0]["selfLink"] if image_url: return image_url else: raise ImageError("Could not find given image id `%s`" % image_id)
def start_instance(self, key_name, public_key_path, private_key_path, security_group, flavor, image_id, image_userdata, 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 = {} 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 '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 need to be spelt out explicitly and cannot be # conflated into `**vm_start_args` 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