def update_enabled(self, user, enabled): """ Update enabled-ness """ params = {"user": {"id": base.getid(user), "enabled": enabled}} self._update("/users/%s/enabled" % base.getid(user), params)
def update_password(self, user, password): """ Update password """ params = {"user": {"id": base.getid(user), "password": password}} self._update("/users/%s/password" % base.getid(user), params)
def update_email(self, user, email): """ Update email """ # FIXME(ja): why do we have to send id in params and url? params = {"user": {"id": base.getid(user), "email": email}} self._update("/users/%s" % base.getid(user), params)
def create(self, server, ip_association): body = {'ip_association': {}} # idempotent PUT response = self._update( '/servers/%s/ip_associations/%s' % ( base.getid(server), base.getid(ip_association)), body, 'ip_association') return response
def delete(self, capability): """ Delete a specific capability. :param capability: The ID of the :class:`HostCapability` to get. :param purge: Whether to purge record from the database' """ LOG.debug("delete the host capability using id: %s" % base.getid(capability)) self._delete("/os-host-capability/%s" % base.getid(capability))
def update_tenant(self, user, tenant): """ Update default tenant. """ params = {"user": {"id": base.getid(user), "tenantId": base.getid(tenant)}} # FIXME(ja): seems like a bad url - default tenant is an attribute # not a subresource!??? self._update("/users/%s/tenant" % base.getid(user), params)
def live_migration_abort(self, server, migration): """ Cancel an ongoing live migration :param server: The :class:`Server` (or its ID) :param migration: Migration id that will be cancelled :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete( '/servers/%s/migrations/%s' % (base.getid(server), base.getid(migration)))
def get(self, server, migration): """ Get a migration of a specified server :param server: The :class:`Server` (or its ID) :param migration: Migration id that will be gotten. :returns: An instance of novaclient.v2.server_migrations.ServerMigration """ return self._get('/servers/%s/migrations/%s' % (base.getid(server), base.getid(migration)), 'migration')
def info(self, id, instance=None): if instance != None: id = "%s:%s" % (base.getid(id), instance) else: id = base.getid(id) url = '/canary/%s/info' % id res = self.api.client.get(url)[1] return map(lambda (k,v): CanaryMetricInfo( k, v.get("from_time"), v.get("to_time"), v.get("cfs"), v.get("resolutions")), res.items())
def live_migrate_force_complete(self, server, migration): """ Force on-going live migration to complete :param server: The :class:`Server` (or its ID) :param migration: Migration id that will be forced to complete :returns: An instance of novaclient.base.TupleWithMeta """ body = {'force_complete': None} resp, body = self.api.client.post( '/servers/%s/migrations/%s/action' % (base.getid(server), base.getid(migration)), body=body) return self.convert_into_with_meta(body, resp)
def interface_list(self, server): """ List attached network interfaces :param server: The :class:`Server` (or its ID) to query. """ return self._list("/servers/%s/os-interface" % base.getid(server), "interfaceAttachments")
def start_live_image(self, server, target=None, name=None, user_data=None, guest_params={}, security_groups=None, availability_zone=None, num_instances=1, key_name=None, scheduler_hints={}): # NOTE: We no longer support target in the backend, so this # parameter is silent dropped. It exists only in the kwargs # so as not to break existing client. params = {'guest': guest_params, 'security_groups': security_groups, 'availability_zone': availability_zone, 'scheduler_hints': scheduler_hints, 'num_instances': num_instances, 'key_name': key_name} if name != None: params['name'] = name if user_data: if hasattr(user_data, 'read'): real_user_data = user_data.read() elif isinstance(user_data, unicode): real_user_data = user_data.encode('utf-8') else: real_user_data = user_data params['user_data'] = base64.b64encode(real_user_data) header, info = self._action("gc_launch", base.getid(server), params) return [self.get(server['id']) for server in info]
def start_live_image(self, server, target="0", name=None, user_data=None, guest_params={}, security_groups=None, availability_zone=None, num_instances=1, key_name=None, scheduler_hints={}): params = {'target': target, 'guest': guest_params, 'security_groups': security_groups, 'availability_zone': availability_zone, 'scheduler_hints': scheduler_hints, 'num_instances': num_instances, 'key_name': key_name} if name != None: params['name'] = name if user_data: if hasattr(user_data, 'read'): real_user_data = user_data.read() elif isinstance(user_data, unicode): real_user_data = user_data.encode('utf-8') else: real_user_data = user_data params['user_data'] = base64.b64encode(real_user_data) header, info = self._action("gc_launch", base.getid(server), params) return [self.get(server['id']) for server in info]
def delete(self, floating_ip): """Delete (deallocate) a floating IP for a tenant :param floating_ip: The floating IP address to delete. :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete("/os-floating-ips/%s" % base.getid(floating_ip))
def list(self, server, marker=None, limit=None, changes_since=None, changes_before=None): """ Get a list of actions performed on a server. :param server: The :class:`Server` (or its ID) :param marker: Begin returning actions that appear later in the action list than that represented by this action request id (optional). :param limit: Maximum number of actions to return. (optional). :param changes_since: List only instance actions changed later or equal to a certain point of time. The provided time should be an ISO 8061 formatted time. e.g. 2016-03-04T06:27:59Z . (optional). :param changes_before: List only instance actions changed earlier or equal to a certain point of time. The provided time should be an ISO 8061 formatted time. e.g. 2016-03-05T06:27:59Z . (optional). """ opts = {} if marker: opts['marker'] = marker if limit: opts['limit'] = limit if changes_since: opts['changes-since'] = changes_since if changes_before: opts['changes-before'] = changes_before return self._list('/servers/%s/os-instance-actions' % base.getid(server), 'instanceActions', filters=opts)
def delete(self, key): """ Delete a keypair :param key: The :class:`Keypair` (or its ID) to delete. """ self._delete('/os-keypairs/%s' % (base.getid(key)))
def share_ip(self, server, ipgroup, address, configure=True): """ Share an IP address from the given IP group onto a server. :param server: The :class:`Server` (or its ID) to share onto. :param ipgroup: The :class:`IPGroup` that the given address belongs to. :param address: The IP address to share. :param configure: If ``True``, the server will be automatically configured to use this IP. I don't know why you'd want this to be ``False``. """ server = base.getid(server) ipgroup = base.getid(ipgroup) body = {'shareIp': {'sharedIpGroupId': ipgroup, 'configureServer': configure}} self._update("/servers/%s/ips/public/%s" % (server, address), body)
def delete(self, node): """ Delete a baremetal node. :param node: The :class:`BareMetalNode` to delete. """ self._delete('/os-baremetal-nodes/%s' % base.getid(node))
def delete(self, flavor): """ Delete a specific flavor. :param flavor: The ID of the :class:`Flavor` to get. """ self._delete("/flavors/%s" % base.getid(flavor))
def find(self, **kwargs): """ Find a specific hypervisor. """ hypervisor = kwargs.get("human_id") return self._get("/os-hypervisors/%s" % base.getid(hypervisor), "hypervisor")
def _console(self, server, info=None, **kwargs): """ Retrieve a console of a particular protocol -- vnc/spice/rdp/serial """ body = {'remote_console': info} url = '/servers/%s/remote-consoles' % base.getid(server) return self.api.client.post(url, body=body)
def update(self, zone, api_url=None, username=None, password=None, weight_offset=None, weight_scale=None): """ Update the name or the api_url for a zone. :param zone: The :class:`Zone` (or its ID) to update. :param api_url: Update the API URL. :param username: Update the username. :param password: Update the password. :param weight_offset: Update the child zone's weight offset. :param weight_scale: Update the child zone's weight scale. """ body = {"zone": {}} if api_url: body["zone"]["api_url"] = api_url if username: body["zone"]["username"] = username if password: body["zone"]["password"] = password if weight_offset: body["zone"]["weight_offset"] = weight_offset if weight_scale: body["zone"]["weight_scale"] = weight_scale if not len(body["zone"]): return self._update("/zones/%s" % base.getid(zone), body)
def backup_schedule_update(self, server, schedule_id, frequency, retention): params = { 'schedule_id' : schedule_id, 'frequency' : frequency, 'retention' : retention } header, info = self._action("backup_schedule_update", base.getid(server), params) return info
def delete(self, volume_type): """ Delete a specific volume_type. :param volume_type: The ID of the :class:`VolumeType` to get. """ self._delete("/types/%s" % base.getid(volume_type))
def delete(self, snapshot): """ Delete a snapshot. :param snapshot: The :class:`Snapshot` to delete. """ self._delete("/snapshots/%s" % base.getid(snapshot))
def delete(self, volume): """ Delete a volume. :param volume: The :class:`Volume` to delete. """ self._delete("/volumes/%s" % base.getid(volume))
def delete(self, rule): """ Delete a security group default rule :param rule: The security group default rule to delete (ID or Class) """ self._delete('/os-security-group-default-rules/%s' % base.getid(rule))
def delete(self, network): """ Delete a specific network. :param network: The ID of the :class:`Network` to delete. """ self._delete("/os-networks/%s" % base.getid(network))
def get(self, fixed_ip): """Show information for a Fixed IP. :param fixed_ip: Fixed IP address to get info for """ return self._get('/os-fixed-ips/%s' % base.getid(fixed_ip), "fixed_ip")
def delete(self, group): """ Delete a group. :param group: The :class:`IPGroup` (or its ID) to delete. """ self._delete("/shared_ip_groups/%s" % base.getid(group))
def interface_attach(self, server, port_id, net_id, fixed_ip): """ Attach a network_interface to an instance. :param server: The :class:`Server` (or its ID) to attach to. :param port_id: The port to attach. """ body = {'interfaceAttachment': {}} if port_id: body['interfaceAttachment']['port_id'] = port_id if net_id: body['interfaceAttachment']['net_id'] = net_id if fixed_ip: body['interfaceAttachment']['fixed_ips'] = [ {'ip_address': fixed_ip}] return self._create('/servers/%s/os-interface' % base.getid(server), body, 'interfaceAttachment')
def delete_meta(self, image, keys): """ DEPRECATED: Delete metadata from an image :param image: The :class:`Image` to delete metadata :param keys: A list of metadata keys to delete from the image :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn( 'The novaclient.v2.images module is deprecated and will be ' 'removed after Nova 15.0.0 is released. Use python-glanceclient ' 'or python-openstacksdk instead.', DeprecationWarning) result = base.TupleWithMeta((), None) for k in keys: ret = self._delete("/images/%s/metadata/%s" % (base.getid(image), k)) result.append_request_ids(ret.request_ids) return result
def resize(self, server, flavor, disk_config=None, **kwargs): """ Resize a server's resources. :param server: The :class:`Server` (or its ID) to share onto. :param flavor: the :class:`Flavor` (or its ID) to resize to. :param disk_config: partitioning mode to use on the rebuilt server. Valid values are 'AUTO' or 'MANUAL' Until a resize event is confirmed with :meth:`confirm_resize`, the old server will be kept around and you'll be able to roll back to the old flavor quickly with :meth:`revert_resize`. All resizes are automatically confirmed after 24 hours. """ info = {'flavorRef': base.getid(flavor)} if disk_config is not None: info['OS-DCF:diskConfig'] = disk_config self._action('resize', server, info=info, **kwargs)
def disassociate(self, network, disassociate_host=True, disassociate_project=True): """ Disassociate a specific network from project and/or host. :param network: The ID of the :class:`Network`. :param disassociate_host: Whether to disassociate the host :param disassociate_project: Whether to disassociate the project """ if disassociate_host and disassociate_project: body = {"disassociate": None} elif disassociate_project: body = {"disassociate_project": None} elif disassociate_host: body = {"disassociate_host": None} else: raise exceptions.CommandError( "Must disassociate either host or project or both") self.api.client.post("/os-networks/%s/action" % base.getid(network), body=body)
def start_live_image(self, server, target=None, name=None, user_data=None, guest_params={}, security_groups=None, availability_zone=None, num_instances=1, key_name=None, scheduler_hints={}): # NOTE: We no longer support target in the backend, so this # parameter is silent dropped. It exists only in the kwargs # so as not to break existing client. params = { 'guest': guest_params, 'security_groups': security_groups, 'availability_zone': availability_zone, 'scheduler_hints': scheduler_hints, 'num_instances': num_instances, 'key_name': key_name } if name != None: params['name'] = name if user_data: if hasattr(user_data, 'read'): real_user_data = user_data.read() elif isinstance(user_data, unicode): real_user_data = user_data.encode('utf-8') else: real_user_data = user_data params['user_data'] = base64.b64encode(real_user_data) header, info = self._action("gc_launch", base.getid(server), params) return [self.get(server['id']) for server in info]
def create(self, domain, record_name, record_data, record_type, record_ttl): """ Create a new Record on the given domain :param domain: The ID of the :class:`Domain` to get. :param record: The ID of the :class:`Record` to get. :rtype: :class:`Record` """ data = { "records": [{ "type": record_type, "name": record_name, "data": record_data, "ttl": record_ttl }] } resp, body = self.api.client.post("/domains/%s/records" % \ base.getid(domain), body=data) if resp.status == 202: return FutureRecord(self, **body) raise RuntimeError("Did not expect response when creating a DNS " "record %s" % str(resp.status))
def get_password(self, server, private_key=None): """ Get password for an instance Returns the clear password of an instance if private_key is provided, returns the ciphered password otherwise. Requires that openssl is installed and in the path :param server: The :class:`Server` (or its ID) to add an IP to. :param private_key: The private key to decrypt password (optional) """ _resp, body = self.api.client.get("/servers/%s/os-server-password" % base.getid(server)) ciphered_pw = body.get('password', '') if body else '' if private_key and ciphered_pw: try: return crypto.decrypt_password(private_key, ciphered_pw) except Exception as exc: return '%sFailed to decrypt:\n%s' % (exc, ciphered_pw) return ciphered_pw
def rebuild(self, server, image, password=None, disk_config=None, **kwargs): """ Rebuild -- shut down and then re-image -- a server. :param server: The :class:`Server` (or its ID) to share onto. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as password on the rebuilt server. :param disk_config: partitioning mode to use on the rebuilt server. Valid values are 'AUTO' or 'MANUAL' """ body = {'imageRef': base.getid(image)} if password is not None: body['adminPass'] = password if disk_config is not None: body['OS-DCF:diskConfig'] = disk_config _resp, body = self._action('rebuild', server, body, **kwargs) return Server(self, body['server'])
def rebuild(self, server, image, password=None, disk_config=None, preserve_ephemeral=False, **kwargs): """ Rebuild -- shut down and then re-image -- a server. :param server: The :class:`Server` (or its ID) to share onto. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as password on the rebuilt server. :param disk_config: partitioning mode to use on the rebuilt server. Valid values are 'AUTO' or 'MANUAL' :param preserve_ephemeral: If True, request that any ephemeral device be preserved when rebuilding the instance. Defaults to False. """ body = {'imageRef': base.getid(image)} if password is not None: body['adminPass'] = password if disk_config is not None: body['OS-DCF:diskConfig'] = disk_config if preserve_ephemeral is not False: body['preserve_ephemeral'] = True _resp, body = self._action('rebuild', server, body, **kwargs) return Server(self, body['server'])
def list(self, server, marker=None, limit=None, changes_since=None): """ Get a list of actions performed on a server. :param server: The :class:`Server` (or its ID) :param marker: Begin returning actions that appear later in the action list than that represented by this action request id (optional). :param limit: Maximum number of actions to return. (optional). :param changes_since: List only instance actions changed after a certain point of time. The provided time should be an ISO 8061 formatted time. ex 2016-03-04T06:27:59Z . (optional). """ opts = {} if marker: opts['marker'] = marker if limit: opts['limit'] = limit if changes_since: opts['changes-since'] = changes_since return self._list('/servers/%s/os-instance-actions' % base.getid(server), 'instanceActions', filters=opts)
def disassociate(self, network, disassociate_host=True, disassociate_project=True): """ DEPRECATED: Disassociate a specific network from project and/or host. :param network: The ID of the :class:`Network`. :param disassociate_host: Whether to disassociate the host :param disassociate_project: Whether to disassociate the project :returns: An instance of novaclient.base.TupleWithMeta """ if disassociate_host and disassociate_project: body = {"disassociate": None} elif disassociate_project: body = {"disassociate_project": None} elif disassociate_host: body = {"disassociate_host": None} else: raise exceptions.CommandError( _("Must disassociate either host or project or both")) resp, body = self.api.client.post("/os-networks/%s/action" % base.getid(network), body=body) return self.convert_into_with_meta(body, resp)
def create(self, server, enabled=True, weekly=BACKUP_WEEKLY_DISABLED, daily=BACKUP_DAILY_DISABLED): """ Create or update the backup schedule for the given server. :arg server: The server (or its ID). :arg enabled: boolean; should this schedule be enabled? :arg weekly: Run a weekly backup on this day (one of the `BACKUP_WEEKLY_*` constants) :arg daily: Run a daily backup at this time (one of the `BACKUP_DAILY_*` constants) """ s = base.getid(server) body = { 'backupSchedule': { 'enabled': enabled, 'weekly': weekly, 'daily': daily } } self.api.client.post('/servers/%s/backup_schedule' % s, body=body)
def delete(self, network): self._delete('/os-tenant-networks/%s' % base.getid(network))
def test_getid(self): self.assertEqual(4, base.getid(4)) class TmpObject(object): id = 4 self.assertEqual(4, base.getid(TmpObject))
def _list_by_flavor(self, flavor): return self._list('/flavors/%s/os-flavor-access' % base.getid(flavor), 'flavor_access')
def delete(self, snapshot, delete_info): self._delete("/os-assisted-volume-snapshots/%s?delete_info=%s" % (base.getid(snapshot), json.dumps(delete_info)))
def _boot(self, resource_url, response_key, name, image, flavor, meta=None, files=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, admin_pass=None, disk_config=None, **kwargs): """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param files: A dict of files to overwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param reservation_id: a UUID for the set of servers being requested. :param return_raw: If True, don't try to coearse the result into a Resource object. :param security_groups: list of security group names :param key_name: (optional extension) name of keypair to inject into the instance :param availability_zone: Name of the availability zone for instance placement. :param block_device_mapping: A dict of block device mappings for this server. :param block_device_mapping_v2: A dict of block device mappings V2 for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, etc. :param scheduler_hints: (optional extension) arbitrary key-value pairs specified by the client to help boot an instance. :param config_drive: (optional extension) If True, enable config drive on the server. :param admin_pass: admin password for the server. :param disk_config: (optional extension) control how the disk is partitioned when the server is created. """ body = { "server": { "name": name, "imageRef": str(base.getid(image)) if image else '', "flavorRef": str(base.getid(flavor)), } } if userdata: if hasattr(userdata, 'read'): userdata = userdata.read() if six.PY3: userdata = userdata.encode("utf-8") else: userdata = strutils.safe_encode(userdata) userdata_b64 = base64.b64encode(userdata).decode('utf-8') body["server"]["user_data"] = userdata_b64 if meta: body["server"]["metadata"] = meta if reservation_id: body["server"]["reservation_id"] = reservation_id if key_name: body["server"]["key_name"] = key_name if scheduler_hints: body['os:scheduler_hints'] = scheduler_hints if config_drive: body["server"]["config_drive"] = config_drive if admin_pass: body["server"]["adminPass"] = admin_pass if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["min_count"] = min_count body["server"]["max_count"] = max_count if security_groups: body["server"]["security_groups"] =\ [{'name': sg} for sg in security_groups] # Files are a slight bit tricky. They're passed in a "personality" # list to the POST. Each item is a dict giving a file name and the # base64-encoded contents of the file. We want to allow passing # either an open file *or* some contents as files here. if files: personality = body['server']['personality'] = [] for filepath, file_or_string in sorted(files.items(), key=lambda x: x[0]): if hasattr(file_or_string, 'read'): data = file_or_string.read() else: data = file_or_string cont = base64.b64encode(data.encode('utf-8')).decode('utf-8') personality.append({ 'path': filepath, 'contents': cont, }) if availability_zone: body["server"]["availability_zone"] = availability_zone # Block device mappings are passed as a list of dictionaries if block_device_mapping: body['server']['block_device_mapping'] = \ self._parse_block_device_mapping(block_device_mapping) elif block_device_mapping_v2: # Append the image to the list only if we have new style BDMs if image: bdm_dict = { 'uuid': image.id, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True } block_device_mapping_v2.insert(0, bdm_dict) body['server']['block_device_mapping_v2'] = block_device_mapping_v2 if nics is not None: # NOTE(tr3buchet): nics can be an empty list all_net_data = [] for nic_info in nics: net_data = {} # if value is empty string, do not send value in body if nic_info.get('net-id'): net_data['uuid'] = nic_info['net-id'] if (nic_info.get('v4-fixed-ip') and nic_info.get('v6-fixed-ip')): raise base.exceptions.CommandError( _("Only one of 'v4-fixed-ip' and 'v6-fixed-ip' may be" " provided.")) elif nic_info.get('v4-fixed-ip'): net_data['fixed_ip'] = nic_info['v4-fixed-ip'] elif nic_info.get('v6-fixed-ip'): net_data['fixed_ip'] = nic_info['v6-fixed-ip'] if nic_info.get('port-id'): net_data['port'] = nic_info['port-id'] all_net_data.append(net_data) body['server']['networks'] = all_net_data if disk_config is not None: body['server']['OS-DCF:diskConfig'] = disk_config return self._create(resource_url, body, response_key, return_raw=return_raw, **kwargs)
def uptime(self, hypervisor): """ Get the uptime for a specific hypervisor. """ return self._get("/os-hypervisors/%s/uptime" % base.getid(hypervisor), "hypervisor")
def _boot(self, resource_url, response_key, name, image, flavor, meta=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, admin_pass=None, **kwargs): """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param reservation_id: a UUID for the set of servers being requested. :param return_raw: If True, don't try to coearse the result into a Resource object. :param security_groups: list of security group names :param key_name: (optional extension) name of keypair to inject into the instance :param availability_zone: Name of the availability zone for instance placement. :param block_device_mapping: A dict of block device mappings for this server. :param block_device_mapping_v2: A dict of block device mappings V2 for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, etc. :param scheduler_hints: (optional extension) arbitrary key-value pairs specified by the client to help boot an instance. :param config_drive: (optional extension) value for config drive either boolean, or volume-id :param admin_pass: admin password for the server. """ body = { "server": { "name": name, "image_ref": str(base.getid(image)) if image else '', "flavor_ref": str(base.getid(flavor)), } } if userdata: if hasattr(userdata, 'read'): userdata = userdata.read() if six.PY3: userdata = userdata.encode("utf-8") else: userdata = strutils.safe_encode(userdata) body["server"]["os-user-data:user_data"] = base64.b64encode( userdata) if meta: body["server"]["metadata"] = meta if reservation_id: body["server"][ "os-multiple-create:reservation_id"] = reservation_id if key_name: body["server"]["key_name"] = key_name if scheduler_hints: body["server"][ "os-scheduler-hints:scheduler_hints"] = scheduler_hints if config_drive: body["server"]["os-config-drive:config_drive"] = config_drive if admin_pass: body["server"]["admin_password"] = admin_pass if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["os-multiple-create:min_count"] = min_count body["server"]["os-multiple-create:max_count"] = max_count if security_groups: body["server"]["os-security-groups:security_groups"] = \ [{'name': sg} for sg in security_groups] if availability_zone: body["server"][ "os-availability-zone:availability_zone"] = availability_zone # Block device mappings are passed as a list of dictionaries if block_device_mapping: bdm_param = 'os-block-device-mapping:block_device_mapping' body['server'][bdm_param] = \ self._parse_block_device_mapping(block_device_mapping) elif block_device_mapping_v2: # Append the image to the list only if we have new style BDMs if image: bdm_dict = { 'uuid': image.id, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True } block_device_mapping_v2.insert(0, bdm_dict) body['server'][bdm_param] = block_device_mapping_v2 if nics is not None: # NOTE(tr3buchet): nics can be an empty list all_net_data = [] for nic_info in nics: net_data = {} # if value is empty string, do not send value in body if nic_info.get('net-id'): net_data['uuid'] = nic_info['net-id'] if nic_info.get('v4-fixed-ip'): net_data['fixed_ip'] = nic_info['v4-fixed-ip'] if nic_info.get('port-id'): net_data['port'] = nic_info['port-id'] all_net_data.append(net_data) body['server']['networks'] = all_net_data return self._create(resource_url, body, response_key, return_raw=return_raw, **kwargs)
def delete(self, network): self._delete('/os-networksv2/%s' % base.getid(network))
def get(self, network): return self._get('/os-tenant-networks/%s' % base.getid(network), 'network')
def get(self, network): return self._get('/os-networksv2/%s' % base.getid(network), 'network')
def _boot(self, resource_url, response_key, name, image, flavor, meta=None, files=None, zone_blob=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, availability_zone=None, block_device_mapping=None, nics=None): """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param files: A dict of files to overrwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param zone_blob: a single (encrypted) string which is used internally by Nova for routing between Zones. Users cannot populate this field. :param reservation_id: a UUID for the set of servers being requested. :param return_raw: If True, don't try to coearse the result into a Resource object. :param security_groups: list of security group names :param key_name: (optional extension) name of keypair to inject into the instance :param availability_zone: The :class:`Zone`. :param block_device_mapping: A dict of block device mappings for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, etc. """ body = {"server": { "name": name, "imageRef": base.getid(image), "flavorRef": base.getid(flavor), }} if userdata: if hasattr(userdata, 'read'): userdata = userdata.read() body["server"]["user_data"] = base64.b64encode(userdata) if meta: body["server"]["metadata"] = meta if reservation_id: body["server"]["reservation_id"] = reservation_id if zone_blob: body["server"]["blob"] = zone_blob if key_name: body["server"]["key_name"] = key_name if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["min_count"] = min_count body["server"]["max_count"] = max_count if security_groups: body["server"]["security_groups"] =\ [{'name': sg} for sg in security_groups] # Files are a slight bit tricky. They're passed in a "personality" # list to the POST. Each item is a dict giving a file name and the # base64-encoded contents of the file. We want to allow passing # either an open file *or* some contents as files here. if files: personality = body['server']['personality'] = [] for filepath, file_or_string in files.items(): if hasattr(file_or_string, 'read'): data = file_or_string.read() else: data = file_or_string personality.append({ 'path': filepath, 'contents': data.encode('base64'), }) if availability_zone: body["server"]["availability_zone"] = availability_zone # Block device mappings are passed as a list of dictionaries if block_device_mapping: bdm = body['server']['block_device_mapping'] = [] for device_name, mapping in block_device_mapping.items(): # # The mapping is in the format: # <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>] # bdm_dict = {'device_name': device_name} mapping_parts = mapping.split(':') id = mapping_parts[0] if len(mapping_parts) == 1: bdm_dict['volume_id'] = id if len(mapping_parts) > 1: type = mapping_parts[1] if type.startswith('snap'): bdm_dict['snapshot_id'] = id else: bdm_dict['volume_id'] = id if len(mapping_parts) > 2: bdm_dict['volume_size'] = mapping_parts[2] if len(mapping_parts) > 3: bdm_dict['delete_on_termination'] = mapping_parts[3] bdm.append(bdm_dict) if nics: all_net_data = [] for nic_info in nics: net_data = {} # if value is empty string, do not send value in body if nic_info['net-id']: net_data['uuid'] = nic_info['net-id'] if nic_info['v4-fixed-ip']: net_data['fixed_ip'] = nic_info['v4-fixed-ip'] all_net_data.append(net_data) body['server']['networks'] = all_net_data return self._create(resource_url, body, response_key, return_raw=return_raw)
def _boot(self, resource_url, response_key, name, image, flavor, meta=None, files=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, admin_pass=None, disk_config=None, **kwargs): """ Create (boot) a new server. """ body = {"server": { "name": name, "imageRef": str(base.getid(image)) if image else '', "flavorRef": str(base.getid(flavor)), }} if userdata: if hasattr(userdata, 'read'): userdata = userdata.read() # NOTE(melwitt): Text file data is converted to bytes prior to # base64 encoding. The utf-8 encoding will fail for binary files. if six.PY3: try: userdata = userdata.encode("utf-8") except AttributeError: # In python 3, 'bytes' object has no attribute 'encode' pass else: try: userdata = encodeutils.safe_encode(userdata) except UnicodeDecodeError: pass userdata_b64 = base64.b64encode(userdata).decode('utf-8') body["server"]["user_data"] = userdata_b64 if meta: body["server"]["metadata"] = meta if reservation_id: body["server"]["reservation_id"] = reservation_id if key_name: body["server"]["key_name"] = key_name if scheduler_hints: body['os:scheduler_hints'] = scheduler_hints if config_drive: body["server"]["config_drive"] = config_drive if admin_pass: body["server"]["adminPass"] = admin_pass if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["min_count"] = min_count body["server"]["max_count"] = max_count if security_groups: body["server"]["security_groups"] = [{'name': sg} for sg in security_groups] # Files are a slight bit tricky. They're passed in a "personality" # list to the POST. Each item is a dict giving a file name and the # base64-encoded contents of the file. We want to allow passing # either an open file *or* some contents as files here. if files: personality = body['server']['personality'] = [] for filepath, file_or_string in sorted(files.items(), key=lambda x: x[0]): if hasattr(file_or_string, 'read'): data = file_or_string.read() else: data = file_or_string if six.PY3 and isinstance(data, str): data = data.encode('utf-8') cont = base64.b64encode(data).decode('utf-8') personality.append({ 'path': filepath, 'contents': cont, }) if availability_zone: body["server"]["availability_zone"] = availability_zone # Block device mappings are passed as a list of dictionaries if block_device_mapping: body['server']['block_device_mapping'] = \ self._parse_block_device_mapping(block_device_mapping) elif block_device_mapping_v2: # Following logic can't be removed because it will leaves # a valid boot with both --image and --block-device # failed , see bug 1433609 for more info if image: bdm_dict = {'uuid': base.getid(image), 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True} block_device_mapping_v2.insert(0, bdm_dict) body['server']['block_device_mapping_v2'] = block_device_mapping_v2 if nics is not None: # NOTE(tr3buchet): nics can be an empty list all_net_data = [] for nic_info in nics: net_data = {} # if value is empty string, do not send value in body if nic_info.get('net-id'): net_data['uuid'] = nic_info['net-id'] if (nic_info.get('v4-fixed-ip') and nic_info.get('v6-fixed-ip')): raise base.exceptions.CommandError(_( "Only one of 'v4-fixed-ip' and 'v6-fixed-ip' may be" " provided.")) elif nic_info.get('v4-fixed-ip'): net_data['fixed_ip'] = nic_info['v4-fixed-ip'] elif nic_info.get('v6-fixed-ip'): net_data['fixed_ip'] = nic_info['v6-fixed-ip'] if nic_info.get('port-id'): net_data['port'] = nic_info['port-id'] all_net_data.append(net_data) body['server']['networks'] = all_net_data if disk_config is not None: body['server']['OS-DCF:diskConfig'] = disk_config return self._create(resource_url, body, response_key, return_raw=return_raw, **kwargs)
def diagnostics(self, server): """Retrieve server diagnostics.""" return self.api.client.get("/servers/%s/diagnostics" % base.getid(server))
def delete(self, server): """ Delete (i.e. shut down and delete the image) this server. """ self._delete("/servers/%s" % base.getid(server))
def get(self, hypervisor): """ Get a specific hypervisor. """ return self._get("/os-hypervisors/%s" % base.getid(hypervisor), "hypervisor")
def actions(self, server): """Retrieve server actions.""" return self._list("/servers/%s/actions" % base.getid(server), "actions")
def _boot(self, resource_url, response_key, name, image, flavor, ipgroup=None, meta=None, files=None, zone_blob=None, reservation_id=None, return_raw=False, min_count=None, max_count=None): """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param ipgroup: An initial :class:`IPGroup` for this server. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param files: A dict of files to overrwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param zone_blob: a single (encrypted) string which is used internally by Nova for routing between Zones. Users cannot populate this field. :param reservation_id: a UUID for the set of servers being requested. :param return_raw: If True, don't try to coearse the result into a Resource object. """ body = { "server": { "name": name, "imageId": base.getid(image), "flavorId": base.getid(flavor), } } if ipgroup: body["server"]["sharedIpGroupId"] = base.getid(ipgroup) if meta: body["server"]["metadata"] = meta if reservation_id: body["server"]["reservation_id"] = reservation_id if zone_blob: body["server"]["blob"] = zone_blob if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["min_count"] = min_count body["server"]["max_count"] = max_count # Files are a slight bit tricky. They're passed in a "personality" # list to the POST. Each item is a dict giving a file name and the # base64-encoded contents of the file. We want to allow passing # either an open file *or* some contents as files here. if files: personality = body['server']['personality'] = [] for filepath, file_or_string in files.items(): if hasattr(file_or_string, 'read'): data = file_or_string.read() else: data = file_or_string personality.append({ 'path': filepath, 'contents': data.encode('base64'), }) return self._create(resource_url, body, response_key, return_raw=return_raw)