Source code for ecl.compute.v2.server

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from ecl.compute import compute_service
from ecl.compute.v2 import metadata as _metadata
from ecl import exceptions
from ecl import resource2
from ecl import utils


[docs]class Server(resource2.Resource, _metadata.MetadataMixin): resource_key = 'server' resources_key = 'servers' base_path = '/servers' service = compute_service.ComputeService() # capabilities allow_create = True allow_get = True allow_update = True allow_delete = True allow_list = True _query_mapping = resource2.QueryParameters("image", "flavor", "name", "status", "host", changes_since="changes-since") #: IPv4 Address access_ipv4 = resource2.Body('accessIPv4') accessIPv4 = resource2.Body('accessIPv4') #: IPv6 Address access_ipv6 = resource2.Body('accessIPv6') accessIPv6 = resource2.Body('accessIPv6') #: A dictionary of addresses this server can be accessed through. #: The dictionary contains keys such as ``private`` and ``public``, #: each containing a list of dictionaries for addresses of that type. #: The addresses are contained in a dictionary with keys ``addr`` #: and ``version``, which is either 4 or 6 depending on the protocol #: of the IP address. *Type: dict* addresses = resource2.Body('addresses', type=dict) #: config drive config_drive = resource2.Body('config_drive') #: Timestamp of when the server was created. created_at = resource2.Body('created') #: The flavor reference, as a ID or full URL, for the flavor to use for #: this server. flavor_id = resource2.Body('flavorRef') #: The flavor property as returned from server. flavor = resource2.Body('flavor', type=dict) #: An ID representing the host of this server. host_id = resource2.Body('hostId') #: The image reference, as a ID or full URL, for the image to use for #: this server. image_id = resource2.Body('imageRef') #: The image property as returned from server. image = resource2.Body('image', type=dict) #: ID of the server id = resource2.Body('id') #: The name of an associated keypair key_name = resource2.Body('key_name') #: A list of dictionaries holding links relevant to this server. links = resource2.Body('links') #: Metadata stored for this server. *Type: dict* metadata = resource2.Body('metadata', type=dict) #: Name of the server name = resource2.Body('name') #: A networks object. Required parameter when there are multiple #: networks defined for the tenant. When you do not specify the #: networks parameter, the server attaches to the only network #: created for the current tenant. networks = resource2.Body('networks', type=dict) #: The disk configuration. Either AUTO or MANUAL. disk_config = resource2.Body('OS-DCF:diskConfig') diskConfig = resource2.Body('OS-DCF:diskConfig') #: The name of the availability zone this server is a part of. availability_zone = resource2.Body('OS-EXT-AZ:availability_zone') #: The power state of this server. power_state = resource2.Body('OS-EXT-STS:power_state') #: The task state of this server. task_state = resource2.Body('OS-EXT-STS:task_state') #: The VM state of this server. vm_state = resource2.Body('OS-EXT-STS:vm_state') #: A list of an attached volumes. Each item in the list contains at least #: an "id" key to identify the specific volumes. attached_volumes = resource2.Body( 'os-extended-volumes:volumes_attached') #: The timestamp when the server was launched. launched_at = resource2.Body('OS-SRV-USG:launched_at') #: The timestamp when the server was terminated (if it has been). terminated_at = resource2.Body('OS-SRV-USG:terminated_at') #: of completion. Once it is completed, it will be 100. *Type: int* progress = resource2.Body('progress', type=int) #: A list of applicable security groups. Each group contains keys for #: description, name, id, and rules. security_groups = resource2.Body('security_groups') #: The state this server is in. Valid values include ``ACTIVE``, #: ``BUILDING``, ``DELETED``, ``ERROR``, ``HARD_REBOOT``, ``PASSWORD``, #: ``PAUSED``, ``REBOOT``, ``REBUILD``, ``RESCUED``, ``RESIZED``, #: ``REVERT_RESIZE``, ``SHUTOFF``, ``SOFT_DELETED``, ``STOPPED``, #: ``SUSPENDED``, ``UNKNOWN``, or ``VERIFY_RESIZE``. status = resource2.Body('status') #: The ID of the project this server is associated with. project_id = resource2.Body('tenant_id') #: Timestamp of when this server was last updated. updated_at = resource2.Body('updated') #: The ID of the owners of this server. user_id = resource2.Body('user_id') #: The dict of tags of servers tags = resource2.Body('tags') #: The file path and contents, text only, to inject into the server at #: launch. The maximum size of the file path data is 255 bytes. #: The maximum limit is The number of allowed bytes in the decoded, #: rather than encoded, data. personality = resource2.Body('personality') #: Configuration information or scripts to use upon launch. #: Must be Base64 encoded. user_data = resource2.Body('user_data') #: Enables fine grained control of the block device mapping for an #: instance. This is typically used for booting servers from volumes. block_device_mapping = resource2.Body('block_device_mapping', type=dict) block_device_mapping_v2 = resource2.Body('block_device_mapping_v2', type=dict) #: admin user's password for this server admin_pass = resource2.Body('adminPass') adminPass = resource2.Body('adminPass') #: The dictionary of data to send to the scheduler. scheduler_hints = resource2.Body('os:scheduler_hints') #: Requested minim count of instance min_count = resource2.Body('min_count') #: Requested maximum count of instance max_count = resource2.Body('max_count')
[docs] def create(self, session, prepend_key=True): """Create a remote resource based on this instance. :param session: The session to use for making this request. :type session: :class:`~ecl.session.Session` :param prepend_key: A boolean indicating whether the resource_key should be prepended in a resource creation request. Default to True. :return: This :class:`Resource` instance. :raises: :exc:`~ecl.exceptions.MethodNotSupported` if :data:`Resource.allow_create` is not set to ``True``. """ request = self._prepare_request(requires_id=False, prepend_key=prepend_key) az = request.body['server'].get('OS-EXT-AZ:availability_zone') if 'OS-EXT-AZ:availability_zone' in request.body['server']: del request.body['server']['OS-EXT-AZ:availability_zone'] if az is not None: request.body['server'].update({'availability_zone': az}) response = session.post(request.uri, endpoint_filter=self.service, json=request.body, headers=request.headers) self._translate_response(response) return self
def _action(self, session, body): """Preform server actions given the message body.""" # NOTE: This is using Server.base_path instead of self.base_path # as both Server and ServerDetail instances can be acted on, but # the URL used is sans any additional /detail/ part. url = utils.urljoin(Server.base_path, self.id, 'action') headers = {'Accept': ''} return session.post( url, endpoint_filter=self.service, json=body, headers=headers)
[docs] def create_image(self, session, name, metadata=None): """Create image from server. :param session :param name :param metadata """ action = {'name': name} if metadata is not None: action['metadata'] = metadata body = {'createImage': action} return self._action(session, body)
[docs] def get_console(self, session, vnc_type): action = {'type': vnc_type} body = {'os-getVNCConsole': action} resp = self._action(session, body) if resp: console_dict = resp.json() if console_dict: return console_dict.get("console")
[docs] def start(self, session): body = {"os-start": None} return self._action(session, body)
[docs] def stop(self, session): body = {"os-stop": None} return self._action(session, body)
[docs] def resize(self, session, flavor): """Resize server to flavor reference.""" body = { 'resize': { 'flavorRef': flavor, 'OS-DCF:diskConfig': 'AUTO' } } self._action(session, body)
[docs] def change_password(self, session, new_password): """Change the administrator password to the given password.""" body = {'changePassword': {'adminPass': new_password}} self._action(session, body)
[docs] def reboot(self, session, reboot_type): """Reboot server where reboot_type might be 'SOFT' or 'HARD'.""" body = {'reboot': {'type': reboot_type}} self._action(session, body)
[docs] def force_delete(self, session): """Force delete a server.""" body = {'forceDelete': None} self._action(session, body)
[docs] def rebuild(self, session, name, admin_password, preserve_ephemeral=False, image=None, access_ipv4=None, access_ipv6=None, metadata=None, personality=None): """Rebuild the server with the given arguments.""" action = { 'name': name, 'adminPass': admin_password, 'preserve_ephemeral': preserve_ephemeral } if image is not None: action['imageRef'] = resource2.Resource._get_id(image) if access_ipv4 is not None: action['accessIPv4'] = access_ipv4 if access_ipv6 is not None: action['accessIPv6'] = access_ipv6 if metadata is not None: action['metadata'] = metadata if personality is not None: action['personality'] = personality body = {'rebuild': action} response = self._action(session, body) self._translate_response(response) return self
[docs] def confirm_resize(self, session): """Confirm the resize of the server.""" body = {'confirmResize': None} self._action(session, body)
[docs] def revert_resize(self, session): """Revert the resize of the server.""" body = {'revertResize': None} self._action(session, body)
[docs] def add_security_group(self, session, security_group): body = {"addSecurityGroup": {"name": security_group}} self._action(session, body)
[docs] def remove_security_group(self, session, security_group): body = {"removeSecurityGroup": {"name": security_group}} self._action(session, body)
@classmethod
[docs] def find(cls, session, name_or_id, ignore_missing=True, **params): """Find a resource by its name or id. :param session: The session to use for making this request. :type session: :class:`~ecl.session.Session` :param name_or_id: This resource's identifier, if needed by the request. The default is ``None``. :param bool ignore_missing: When set to ``False`` :class:`~ecl.exceptions.ResourceNotFound` will be raised when the resource does not exist. When set to ``True``, None will be returned when attempting to find a nonexistent resource. :param dict params: Any additional parameters to be passed into underlying methods, such as to :meth:`~ecl.resource2.Resource.existing` in order to pass on URI parameters. :return: The :class:`Resource` object matching the given name or id or None if nothing matches. :raises: :class:`ecl.exceptions.DuplicateResource` if more than one resource is found for this request. :raises: :class:`ecl.exceptions.ResourceNotFound` if nothing is found and ignore_missing is ``False``. """ # Try to short-circuit by looking directly for a matching ID. data = cls.list(session, **params) result = cls._get_one_match(name_or_id, data) if result is not None: return result if ignore_missing: return None raise exceptions.ResourceNotFound( "No %s found for %s" % (cls.__name__, name_or_id))
[docs]class ServerDetail(Server): base_path = '/servers/detail' # capabilities allow_create = False allow_get = False allow_update = False allow_delete = False allow_list = True