From ccd8daa63213c11ecfc6faf6f661ad9ae088cfbf Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Wed, 23 Mar 2016 22:54:13 -0400 Subject: [PATCH] Refactor compute for new resource/proxy This change refactors the compute resources and proxy to work with the newly refactored classes. Additionally, it adds several properties to the Server resource that have been documented since it was originally implemented. Several of these have been requested lately by users, including security_groups, availability_zone, and admin_password. Change-Id: I387c9573234c3ed08eb966e388cc2e1a3d891c2d --- openstack/compute/v2/_proxy.py | 323 ++++++++++-------- openstack/compute/v2/availability_zone.py | 18 +- openstack/compute/v2/extension.py | 18 +- openstack/compute/v2/flavor.py | 32 +- openstack/compute/v2/hypervisor.py | 54 ++- openstack/compute/v2/image.py | 34 +- openstack/compute/v2/keypair.py | 49 ++- openstack/compute/v2/limits.py | 91 ++--- openstack/compute/v2/server.py | 149 +++++--- openstack/compute/v2/server_group.py | 16 +- openstack/compute/v2/server_interface.py | 20 +- openstack/compute/v2/server_ip.py | 48 +-- .../tests/functional/compute/v2/test_image.py | 12 +- .../functional/compute/v2/test_keypair.py | 3 +- .../functional/compute/v2/test_limits.py | 10 +- .../functional/compute/v2/test_server.py | 13 +- .../unit/compute/v2/test_availability_zone.py | 10 +- .../tests/unit/compute/v2/test_extension.py | 5 +- .../tests/unit/compute/v2/test_flavor.py | 12 +- .../tests/unit/compute/v2/test_hypervisor.py | 51 ++- openstack/tests/unit/compute/v2/test_image.py | 17 +- .../tests/unit/compute/v2/test_keypair.py | 22 +- .../tests/unit/compute/v2/test_limits.py | 70 +++- .../tests/unit/compute/v2/test_metadata.py | 8 +- openstack/tests/unit/compute/v2/test_proxy.py | 190 ++++++----- .../tests/unit/compute/v2/test_server.py | 186 +++++----- .../unit/compute/v2/test_server_group.py | 7 +- .../unit/compute/v2/test_server_interface.py | 5 +- .../tests/unit/compute/v2/test_server_ip.py | 125 +++---- 29 files changed, 907 insertions(+), 691 deletions(-) diff --git a/openstack/compute/v2/_proxy.py b/openstack/compute/v2/_proxy.py index b0ea5919a..a8c6da2ea 100644 --- a/openstack/compute/v2/_proxy.py +++ b/openstack/compute/v2/_proxy.py @@ -21,11 +21,11 @@ from openstack.compute.v2 import server as _server from openstack.compute.v2 import server_group as _server_group from openstack.compute.v2 import server_interface as _server_interface from openstack.compute.v2 import server_ip -from openstack import proxy -from openstack import resource +from openstack import proxy2 +from openstack import resource2 -class Proxy(proxy.BaseProxy): +class Proxy(proxy2.BaseProxy): def find_extension(self, name_or_id, ignore_missing=True): """Find a single extension @@ -42,16 +42,13 @@ class Proxy(proxy.BaseProxy): return self._find(extension.Extension, name_or_id, ignore_missing=ignore_missing) - def extensions(self, **query): + def extensions(self): """Retrieve a generator of extensions - :param kwargs \*\*query: Optional query parameters to be sent to limit - the resources being returned. - :returns: A generator of extension instances. :rtype: :class:`~openstack.compute.v2.extension.Extension` """ - return self._list(extension.Extension, paginated=False, **query) + return self._list(extension.Extension, paginated=False) def find_flavor(self, name_or_id, ignore_missing=True): """Find a single flavor @@ -197,7 +194,7 @@ class Proxy(proxy.BaseProxy): if isinstance(res, base): return res else: - return base({"id": res}) + return base(id=res) def get_image_metadata(self, image): """Return a dictionary of metadata for an image @@ -308,30 +305,13 @@ class Proxy(proxy.BaseProxy): return self._find(_keypair.Keypair, name_or_id, ignore_missing=ignore_missing) - def keypairs(self, **query): + def keypairs(self): """Return a generator of keypairs - :param kwargs \*\*query: Optional query parameters to be sent to limit - the resources being returned. - :returns: A generator of keypair objects :rtype: :class:`~openstack.compute.v2.keypair.Keypair` """ - return self._list(_keypair.Keypair, paginated=False, **query) - - def update_keypair(self, keypair, **attrs): - """Update a keypair - - :param keypair: Either the ID of a keypair or a - :class:`~openstack.compute.v2.keypair.Keypair` - instance. - :attrs kwargs: The attributes to update on the keypair represented - by ``keypair``. - - :returns: The updated keypair - :rtype: :class:`~openstack.compute.v2.keypair.Keypair` - """ - return self._update(_keypair.Keypair, keypair, **attrs) + return self._list(_keypair.Keypair, paginated=False) def get_limits(self): """Retrieve limits that are applied to the project's account @@ -434,12 +414,6 @@ class Proxy(proxy.BaseProxy): :returns: A generator of server instances. """ srv = _server.ServerDetail if details else _server.Server - - # Server expects changes-since, but we use an underscore - # so it can be a proper Python name. - if "changes_since" in query: - query["changes-since"] = query.pop("changes_since") - return self._list(srv, paginated=True, **query) def update_server(self, server, **attrs): @@ -455,10 +429,142 @@ class Proxy(proxy.BaseProxy): """ return self._update(_server.Server, server, **attrs) + def change_server_password(self, server, new_password): + """Change the administrator password + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + :param str new_password: The new password to be set. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + server.change_password(self.session, new_password) + + def reboot_server(self, server, reboot_type): + """Reboot a server + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + :param str reboot_type: The type of reboot to perform. + "HARD" and "SOFT" are the current options. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + server.reboot(self.session, reboot_type) + + def rebuild_server(self, server, name, admin_password, **attrs): + """Rebuild a server + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + :param str name: The name of the server + :param str admin_password: The administrator password + :param bool preserve_ephemeral: Indicates whether the server + is rebuilt with the preservation of the ephemeral partition. + *Default: False* + :param str image: The id of an image to rebuild with. *Default: None* + :param str access_ipv4: The IPv4 address to rebuild with. + *Default: None* + :param str access_ipv6: The IPv6 address to rebuild with. + *Default: None* + :param dict metadata: A dictionary of metadata to rebuild with. + *Default: None* + :param list personality: A list of dictionaries, each including a + **path** and **contents** key, to be injected + into the rebuilt server at launch. + *Default: None* + + :returns: The rebuilt :class:`~openstack.compute.v2.server.Server` + instance. + """ + server = self._get_resource(_server.Server, server) + return server.rebuild(self.session, name, admin_password, **attrs) + + def resize_server(self, server, flavor): + """Resize a server + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + :param flavor: Either the ID of a flavor or a + :class:`~openstack.compute.v2.flavor.Flavor` instance. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + flavor_id = resource2.Resource._get_id(flavor) + server.resize(self.session, flavor_id) + + def confirm_server_resize(self, server): + """Confirm a server resize + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + server.confirm_resize(self.session) + + def revert_server_resize(self, server): + """Revert a server resize + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + server.revert_resize(self.session) + + def create_server_image(self, server, name, metadata=None): + """Create an image from a server + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + :param str name: The name of the image to be created. + :param dict metadata: A dictionary of metadata to be set on the image. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + server.create_image(self.session, name, metadata) + + def add_security_group_to_server(self, server, security_group): + """Add a security group to a server + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + :param security_group: Either the ID of a security group or a + :class:`~openstack.network.v2.security_group.SecurityGroup` + instance. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + security_group_id = resource2.Resource._get_id(security_group) + server.add_security_group(self.session, security_group_id) + + def remove_security_group_from_server(self, server, security_group): + """Add a security group to a server + + :param server: Either the ID of a server or a + :class:`~openstack.compute.v2.server.Server` instance. + :param security_group: Either the ID of a security group or a + :class:`~openstack.network.v2.security_group.SecurityGroup` + instance. + + :returns: None + """ + server = self._get_resource(_server.Server, server) + security_group_id = resource2.Resource._get_id(security_group) + server.remove_security_group(self.session, security_group_id) + def wait_for_server(self, server, status='ACTIVE', failures=['ERROR'], interval=2, wait=120): - return resource.wait_for_status(self.session, server, status, - failures, interval, wait) + return resource2.wait_for_status(self.session, server, status, + failures, interval, wait) def create_server_interface(self, server, **attrs): """Create a new server interface from attributes @@ -473,9 +579,9 @@ class Proxy(proxy.BaseProxy): :returns: The results of server interface creation :rtype: :class:`~openstack.compute.v2.server_interface.ServerInterface` """ - server_id = resource.Resource.get_id(server) + server_id = resource2.Resource._get_id(server) return self._create(_server_interface.ServerInterface, - path_args={'server_id': server_id}, **attrs) + server_id=server_id, **attrs) def delete_server_interface(self, server_interface, server=None, ignore_missing=True): @@ -497,13 +603,13 @@ class Proxy(proxy.BaseProxy): :returns: ``None`` """ - if isinstance(server_interface, _server_interface.ServerInterface): - server_id = server_interface.server_id - else: - server_id = resource.Resource.get_id(server) + server_id = self._get_uri_attribute(server_interface, server, + "server_id") + server_interface = resource2.Resource._get_id(server_interface) - self._delete(_server_interface.ServerInterface, server_interface, - path_args={'server_id': server_id}, + self._delete(_server_interface.ServerInterface, + port_id=server_interface, + server_id=server_id, ignore_missing=ignore_missing) def get_server_interface(self, server_interface, server=None): @@ -523,131 +629,58 @@ class Proxy(proxy.BaseProxy): :raises: :class:`~openstack.exceptions.ResourceNotFound` when no resource can be found. """ - if isinstance(server_interface, _server_interface.ServerInterface): - server_id = server_interface.server_id - else: - server_id = resource.Resource.get_id(server) + server_id = self._get_uri_attribute(server_interface, server, + "server_id") + server_interface = resource2.Resource._get_id(server_interface) - return self._get(_server_interface.ServerInterface, server_interface, - path_args={'server_id': server_id}) + return self._get(_server_interface.ServerInterface, + server_id=server_id, port_id=server_interface) - def server_interfaces(self, server, **query): + def server_interfaces(self, server): """Return a generator of server interfaces :param server: The server can be either the ID of a server or a :class:`~openstack.compute.v2.server.Server`. - :param kwargs \*\*query: Optional query parameters to be sent to limit - the resources being returned. :returns: A generator of ServerInterface objects :rtype: :class:`~openstack.compute.v2.server_interface.ServerInterface` """ - server_id = resource.Resource.get_id(server) + server_id = resource2.Resource._get_id(server) return self._list(_server_interface.ServerInterface, paginated=False, - path_args={'server_id': server_id}, - **query) + server_id=server_id) - def find_server_ip(self, name_or_id, ignore_missing=True): - """Find a single server IP - - :param name_or_id: The name or ID of a server IP. - :param bool ignore_missing: When set to ``False`` - :class:`~openstack.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. - :returns: One :class:`~openstack.compute.v2.server_ip.ServerIP` or None - """ - return self._find(server_ip.ServerIP, name_or_id, - ignore_missing=ignore_missing) - - def server_ips(self, **query): + def server_ips(self, server, network_label=None): """Return a generator of server IPs - :param kwargs \*\*query: Optional query parameters to be sent to limit - the resources being returned. + :param server: The server can be either the ID of a server or a + :class:`~openstack.compute.v2.server.Server`. + :param network_label: The name of a particular network to list + IP addresses from. :returns: A generator of ServerIP objects :rtype: :class:`~openstack.compute.v2.server_ip.ServerIP` """ - return self._list(server_ip.ServerIP, paginated=False, **query) + server_id = resource2.Resource._get_id(server) + return self._list(server_ip.ServerIP, paginated=False, + server_id=server_id, network_label=network_label) - def resize_server(self, server, flavor): - """Resize a server - - :param server: Either the ID of a server or a - :class:`~openstack.compute.v2.server.Server` instance. - :param falvor: The ID or name of the flavor used to resize the server. - - :returns: None - """ - server = _server.Server.from_id(server) - server.resize(self.session, flavor) - - def confirm_resize_server(self, server): - """Confirm a pending resize_server action - - :param server: Either the ID of a server or a - :class:`~openstack.compute.v2.server.Server` instance. - - :returns: None - """ - server = _server.Server.from_id(server) - server.confirm_resize(self.session) - - def revert_resize_server(self, server): - """Cancel and revert a pending resize_server action - - :param server: Either the ID of a server or a - :class:`~openstack.compute.v2.server.Server` instance. - - :returns: None - """ - server = _server.Server.from_id(server) - server.revert_resize(self.session) - - def rebuild_server(self, server, image, name=None, admin_password=None, - **attrs): - """Rebuild a server - - :param server: Either the ID of a server or a - :class:`~openstack.compute.v2.server.Server` instance. - :param image: The ID or name or a - :class:`~openstack.compute.v2.image.Image` or full - URL of the image used to rebuild the server with. - :param name: New name for the server. - :param admin_password: New admin password for the server. - :param kwargs \*\*attrs: The attributes to rebuild the server. - - :returns: The rebuilt server - :rtype: :class:`~openstack.compute.v2.server.Server` - """ - if isinstance(image, _image.Image): - image_ref = image.id - else: - image_obj = self.find_image(image) - if image_obj: - image_ref = image_obj.id - else: - # the 'image' could be a full url - image_ref = image - - server = _server.Server.from_id(server) - return server.rebuild(self.session, name, image_ref, admin_password, - **attrs) - - def availability_zones(self, **query): + def availability_zones(self, details=False): """Return a generator of availability zones - :param kwargs \*\*query: Optional query parameters to be sent - to limit the resources being returned. + :param bool details: Return extra details about the availability + zones. This defaults to `False` as it generally + requires extra permission. :returns: A generator of availability zone :rtype: :class:`~openstack.compute.v2.availability_zone. AvailabilityZone` """ - return self._list(availability_zone.AvailabilityZone, - paginated=False, **query) + if details: + az = availability_zone.AvailabilityZoneDetail + else: + az = availability_zone.AvailabilityZone + + return self._list(az, paginated=False) def get_server_metadata(self, server): """Return a dictionary of metadata for a server @@ -773,14 +806,14 @@ class Proxy(proxy.BaseProxy): """ return self._list(_server_group.ServerGroup, paginated=False, **query) - def hypervisors(self, **query): + def hypervisors(self): """Return a generator of hypervisor :returns: A generator of hypervisor :rtype: class: `~openstack.compute.v2.hypervisor.Hypervisor` """ - return self._list(_hypervisor.Hypervisor, paginated=False, **query) + return self._list(_hypervisor.Hypervisor, paginated=False) def find_hypervisor(self, name_or_id, ignore_missing=True): """Find a hypervisor from name or id to get the corresponding info diff --git a/openstack/compute/v2/availability_zone.py b/openstack/compute/v2/availability_zone.py index e40136119..56f49295c 100644 --- a/openstack/compute/v2/availability_zone.py +++ b/openstack/compute/v2/availability_zone.py @@ -11,12 +11,10 @@ # under the License. from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class AvailabilityZone(resource.Resource): - - resource_key = 'availability_zone' +class AvailabilityZone(resource2.Resource): resources_key = 'availabilityZoneInfo' base_path = '/os-availability-zone' @@ -27,10 +25,12 @@ class AvailabilityZone(resource.Resource): # Properties #: name of availability zone - name = resource.prop('zoneName') - + name = resource2.Body('zoneName') #: state of availability zone - state = resource.prop('zoneState') - + state = resource2.Body('zoneState') #: hosts of availability zone - hosts = resource.prop('hosts') + hosts = resource2.Body('hosts') + + +class AvailabilityZoneDetail(AvailabilityZone): + base_path = '/os-availability-zone/detail' diff --git a/openstack/compute/v2/extension.py b/openstack/compute/v2/extension.py index 79c7e0852..6d3681034 100644 --- a/openstack/compute/v2/extension.py +++ b/openstack/compute/v2/extension.py @@ -11,10 +11,10 @@ # under the License. from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class Extension(resource.Resource): +class Extension(resource2.Resource): resource_key = 'extension' resources_key = 'extensions' base_path = '/extensions' @@ -22,20 +22,20 @@ class Extension(resource.Resource): id_attribute = "alias" # capabilities - allow_retrieve = True + allow_get = True allow_list = True # Properties #: A short name by which this extension is also known. - alias = resource.prop('alias') + alias = resource2.Body('alias', alternate_id=True) #: Text describing this extension's purpose. - description = resource.prop('description') + description = resource2.Body('description') #: Links pertaining to this extension. This is a list of dictionaries, #: each including keys ``href`` and ``rel``. - links = resource.prop('links') + links = resource2.Body('links') #: The name of the extension. - name = resource.prop('name') + name = resource2.Body('name') #: A URL pointing to the namespace for this extension. - namespace = resource.prop('namespace') + namespace = resource2.Body('namespace') #: Timestamp when this extension was last updated. - updated_at = resource.prop('updated') + updated_at = resource2.Body('updated') diff --git a/openstack/compute/v2/flavor.py b/openstack/compute/v2/flavor.py index b8095da01..255503f01 100644 --- a/openstack/compute/v2/flavor.py +++ b/openstack/compute/v2/flavor.py @@ -11,10 +11,10 @@ # under the License. from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class Flavor(resource.Resource): +class Flavor(resource2.Resource): resource_key = 'flavor' resources_key = 'flavors' base_path = '/flavors' @@ -22,41 +22,45 @@ class Flavor(resource.Resource): # capabilities allow_create = True - allow_retrieve = True + allow_get = True allow_update = True allow_delete = True allow_list = True + _query_mapping = resource2.QueryParameters("sort_key", "sort_dir", + min_disk="minDisk", + min_ram="minRam") + # Properties #: Links pertaining to this flavor. This is a list of dictionaries, #: each including keys ``href`` and ``rel``. - links = resource.prop('links') + links = resource2.Body('links') #: The name of this flavor. - name = resource.prop('name') + name = resource2.Body('name') #: Size of the disk this flavor offers. *Type: int* - disk = resource.prop('disk', type=int) + disk = resource2.Body('disk', type=int) #: ``True`` if this is a publicly visible flavor. ``False`` if this is #: a private image. *Type: bool* - is_public = resource.prop('os-flavor-access:is_public', type=bool) + is_public = resource2.Body('os-flavor-access:is_public', type=bool) #: The amount of RAM (in MB) this flavor offers. *Type: int* - ram = resource.prop('ram', type=int) + ram = resource2.Body('ram', type=int) #: The number of virtual CPUs this flavor offers. *Type: int* - vcpus = resource.prop('vcpus', type=int) + vcpus = resource2.Body('vcpus', type=int) #: Size of the swap partitions. - swap = resource.prop('swap') + swap = resource2.Body('swap') #: Size of the ephemeral data disk attached to this server. *Type: int* - ephemeral = resource.prop('OS-FLV-EXT-DATA:ephemeral', type=int) + ephemeral = resource2.Body('OS-FLV-EXT-DATA:ephemeral', type=int) #: ``True`` if this flavor is disabled, ``False`` if not. *Type: bool* - is_disabled = resource.prop('OS-FLV-DISABLED:disabled', type=bool) + is_disabled = resource2.Body('OS-FLV-DISABLED:disabled', type=bool) #: The bandwidth scaling factor this flavor receives on the network. - rxtx_factor = resource.prop('rxtx_factor', type=float) + rxtx_factor = resource2.Body('rxtx_factor', type=float) class FlavorDetail(Flavor): base_path = '/flavors/detail' allow_create = False - allow_retrieve = False + allow_get = False allow_update = False allow_delete = False allow_list = True diff --git a/openstack/compute/v2/hypervisor.py b/openstack/compute/v2/hypervisor.py index 971e9bdfe..25293f7e9 100644 --- a/openstack/compute/v2/hypervisor.py +++ b/openstack/compute/v2/hypervisor.py @@ -12,12 +12,10 @@ from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class Hypervisor(resource.Resource): - - name_attribute = 'hypervisor_hostname' +class Hypervisor(resource2.Resource): resource_key = 'hypervisor' resources_key = 'hypervisors' base_path = '/os-hypervisors' @@ -25,15 +23,45 @@ class Hypervisor(resource.Resource): service = compute_service.ComputeService() # capabilities - allow_retrieve = True + allow_get = True allow_list = True # Properties - #: status of hypervisor - status = resource.prop('status') - - #: state of hypervisor - state = resource.prop('state') - - #: name of hypervisor - hypervisor_hostname = resource.prop('hypervisor_hostname') + #: Status of hypervisor + status = resource2.Body('status') + #: State of hypervisor + state = resource2.Body('state') + #: Name of hypervisor + name = resource2.Body('hypervisor_hostname') + #: Service details + service_details = resource2.Body('service') + #: Count of the VCPUs in use + vcpus_used = resource2.Body('vcpus_used') + #: Count of all VCPUs + vcpus = resource2.Body('vcpus') + #: Count of the running virtual machines + running_vms = resource2.Body('running_vms') + #: The type of hypervisor + hypervisor_type = resource2.Body('hypervisor_type') + #: Version of the hypervisor + hypervisor_version = resource2.Body('hypervisor_version') + #: The amount, in gigabytes, of local storage used + local_disk_used = resource2.Body('local_gb_used') + #: The amount, in gigabytes, of the local storage device + local_disk_size = resource2.Body('local_gb') + #: The amount, in gigabytes, of free space on the local storage device + local_disk_free = resource2.Body('free_disk_gb') + #: The amount, in megabytes, of memory + memory_used = resource2.Body('memory_mb_used') + #: The amount, in megabytes, of total memory + memory_size = resource2.Body('memory_mb') + #: The amount, in megabytes, of available memory + memory_free = resource2.Body('free_ram_mb') + #: Measurement of the hypervisor's current workload + current_workload = resource2.Body('current_workload') + #: Information about the hypervisor's CPU + cpu_info = resource2.Body('cpu_info') + #: IP address of the host + host_ip = resource2.Body('host_ip') + #: Disk space available to the scheduler + disk_available = resource2.Body("disk_available_least") diff --git a/openstack/compute/v2/image.py b/openstack/compute/v2/image.py index 704455e90..a0cd539b8 100644 --- a/openstack/compute/v2/image.py +++ b/openstack/compute/v2/image.py @@ -12,48 +12,54 @@ from openstack.compute import compute_service from openstack.compute.v2 import metadata -from openstack import resource +from openstack import resource2 -class Image(resource.Resource, metadata.MetadataMixin): +class Image(resource2.Resource, metadata.MetadataMixin): resource_key = 'image' resources_key = 'images' base_path = '/images' service = compute_service.ComputeService() # capabilities - allow_retrieve = True + allow_get = True allow_delete = True allow_list = True + _query_mapping = resource2.QueryParameters("server", "name", + "status", "type", + min_disk="minDisk", + min_ram="minRam", + changes_since="changes-since") + # Properties #: Links pertaining to this image. This is a list of dictionaries, #: each including keys ``href`` and ``rel``, and optionally ``type``. - links = resource.prop('links') + links = resource2.Body('links') #: The name of this image. - name = resource.prop('name') + name = resource2.Body('name') #: Timestamp when the image was created. - created_at = resource.prop('created') + created_at = resource2.Body('created') #: Metadata pertaining to this image. *Type: dict* - metadata = resource.prop('metadata', type=dict) + metadata = resource2.Body('metadata', type=dict) #: The mimimum disk size. *Type: int* - min_disk = resource.prop('minDisk', type=int) + min_disk = resource2.Body('minDisk', type=int) #: The minimum RAM size. *Type: int* - min_ram = resource.prop('minRam', type=int) + min_ram = resource2.Body('minRam', type=int) #: If this image is still building, its progress is represented here. #: Once an image is created, progres will be 100. *Type: int* - progress = resource.prop('progress', type=int) + progress = resource2.Body('progress', type=int) #: The status of this image. - status = resource.prop('status') + status = resource2.Body('status') #: Timestamp when the image was updated. - updated_at = resource.prop('updated') + updated_at = resource2.Body('updated') #: Size of the image in bytes. *Type: int* - size = resource.prop('OS-EXT-IMG-SIZE:size', type=int) + size = resource2.Body('OS-EXT-IMG-SIZE:size', type=int) class ImageDetail(Image): base_path = '/images/detail' - allow_retrieve = False + allow_get = False allow_delete = False allow_list = True diff --git a/openstack/compute/v2/keypair.py b/openstack/compute/v2/keypair.py index c5f19ee4a..26580068e 100644 --- a/openstack/compute/v2/keypair.py +++ b/openstack/compute/v2/keypair.py @@ -11,12 +11,10 @@ # under the License. from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class Keypair(resource.Resource): - id_attribute = 'name' - name_attribute = None +class Keypair(resource2.Resource): resource_key = 'keypair' resources_key = 'keypairs' base_path = '/os-keypairs' @@ -24,35 +22,36 @@ class Keypair(resource.Resource): # capabilities allow_create = True - allow_retrieve = True - allow_update = True + allow_get = True allow_delete = True allow_list = True # Properties #: The short fingerprint associated with the ``public_key`` for #: this keypair. - fingerprint = resource.prop('fingerprint') + fingerprint = resource2.Body('fingerprint') + # NOTE: There is in fact an 'id' field. However, it's not useful + # because all operations use the 'name' as an identifier. + # Additionally, the 'id' field only appears *after* creation, + # so suddenly you have an 'id' field filled in after the fact, + # and it just gets in the way. We need to cover this up by having + # the name be both our id and name. + #: The id identifying the keypair + id = resource2.Body('name') #: A name identifying the keypair - name = resource.prop('name') + name = resource2.Body('name', alternate_id=True) #: The private key for the keypair - private_key = resource.prop('private_key') + private_key = resource2.Body('private_key') #: The SSH public key that is paired with the server. - public_key = resource.prop('public_key') + public_key = resource2.Body('public_key') - def __init__(self, attrs=None, loaded=False): - if attrs is not None: - if 'keypair' in attrs: - attrs = attrs['keypair'] - super(Keypair, self).__init__(attrs, loaded=loaded) + @classmethod + def list(cls, session, paginated=False): + resp = session.get(cls.base_path, endpoint_filter=cls.service, + headers={"Accept": "application/json"}) + resp = resp.json() + resp = resp[cls.resources_key] - def create(self, session): - """Create a new keypair from this instance. - - This is needed because the name is the id, but we can't create one - with a PUT. That and we need the private_key out of the response. - """ - resp = self.create_by_id(session, self._attrs) - self._attrs = resp - self._reset_dirty() - return self + for data in resp: + value = cls.existing(**data[cls.resource_key]) + yield value diff --git a/openstack/compute/v2/limits.py b/openstack/compute/v2/limits.py index a0f1610bf..7d62fea7f 100644 --- a/openstack/compute/v2/limits.py +++ b/openstack/compute/v2/limits.py @@ -11,96 +11,99 @@ # under the License. from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class AbsoluteLimits(resource.Resource): +class AbsoluteLimits(resource2.Resource): #: The number of key-value pairs that can be set as image metadata. - image_meta = resource.prop("maxImageMeta") + image_meta = resource2.Body("maxImageMeta") #: The maximum number of personality contents that can be supplied. - personality = resource.prop("maxPersonality") + personality = resource2.Body("maxPersonality") #: The maximum size, in bytes, of a personality. - personality_size = resource.prop("maxPersonalitySize") + personality_size = resource2.Body("maxPersonalitySize") #: The maximum amount of security group rules allowed. - security_group_rules = resource.prop("maxSecurityGroupRules") + security_group_rules = resource2.Body("maxSecurityGroupRules") #: The maximum amount of security groups allowed. - security_groups = resource.prop("maxSecurityGroups") + security_groups = resource2.Body("maxSecurityGroups") #: The amount of security groups currently in use. - security_groups_used = resource.prop("totalSecurityGroupsUsed") + security_groups_used = resource2.Body("totalSecurityGroupsUsed") #: The number of key-value pairs that can be set as sever metadata. - server_meta = resource.prop("maxServerMeta") + server_meta = resource2.Body("maxServerMeta") #: The maximum amount of cores. - total_cores = resource.prop("maxTotalCores") + total_cores = resource2.Body("maxTotalCores") #: The amount of cores currently in use. - total_cores_used = resource.prop("totalCoresUsed") + total_cores_used = resource2.Body("totalCoresUsed") #: The maximum amount of floating IPs. - floating_ips = resource.prop("maxTotalFloatingIps") + floating_ips = resource2.Body("maxTotalFloatingIps") #: The amount of floating IPs currently in use. - floating_ips_used = resource.prop("totalFloatingIpsUsed") + floating_ips_used = resource2.Body("totalFloatingIpsUsed") #: The maximum amount of instances. - instances = resource.prop("maxTotalInstances") + instances = resource2.Body("maxTotalInstances") #: The amount of instances currently in use. - instances_used = resource.prop("totalInstancesUsed") + instances_used = resource2.Body("totalInstancesUsed") #: The maximum amount of keypairs. - keypairs = resource.prop("maxTotalKeypairs") + keypairs = resource2.Body("maxTotalKeypairs") #: The maximum RAM size in megabytes. - total_ram = resource.prop("maxTotalRAMSize") + total_ram = resource2.Body("maxTotalRAMSize") #: The RAM size in megabytes currently in use. - total_ram_used = resource.prop("totalRAMUsed") + total_ram_used = resource2.Body("totalRAMUsed") #: The maximum amount of server groups. - server_groups = resource.prop("maxServerGroups") + server_groups = resource2.Body("maxServerGroups") #: The amount of server groups currently in use. - server_groups_used = resource.prop("totalServerGroupsUsed") + server_groups_used = resource2.Body("totalServerGroupsUsed") #: The maximum number of members in a server group. - server_group_members = resource.prop("maxServerGroupMembers") + server_group_members = resource2.Body("maxServerGroupMembers") -class RateLimits(resource.Resource): +class RateLimit(resource2.Resource): #: A list of the specific limits that apply to the ``regex`` and ``uri``. - limits = resource.prop("limit", type=list) + limits = resource2.Body("limit", type=list) #: A regex representing which routes this rate limit applies to. - regex = resource.prop("regex") + regex = resource2.Body("regex") #: A URI representing which routes this rate limit applies to. - uri = resource.prop("uri") + uri = resource2.Body("uri") -class Limits(resource.Resource): +class Limits(resource2.Resource): base_path = "/limits" resource_key = "limits" service = compute_service.ComputeService() - allow_retrieve = True + allow_get = True - absolute = resource.prop("absolute", type=AbsoluteLimits) - rate = resource.prop("rate", type=list) + absolute = resource2.Body("absolute", type=AbsoluteLimits) + rate = resource2.Body("rate", type=list) - def get(self, session, args=None, include_headers=False): + def get(self, session): """Get the Limits resource. :param session: The session to use for making this request. :type session: :class:`~openstack.session.Session` - :param dict args: An optional dict that will be translated into query - strings for retrieving the object when specified. :returns: A Limits instance :rtype: :class:`~openstack.compute.v2.limits.Limits` """ - body = self.get_data_by_id(session, self.id, - include_headers=include_headers) + request = self._prepare_request(requires_id=False, prepend_key=False) - # Split the rates away from absolute limits. We can create - # the `absolute` property and AbsoluteLimits resource directly - # from the body. We have to iterate through the list inside `rate` - # in order to create the RateLimits instances for the `rate` property. - rate_body = body.pop("rate") - self._attrs.update(body) + response = session.get(request.uri, endpoint_filter=self.service) + + body = response.json() + body = body[self.resource_key] + + absolute_body = self._transpose_component( + body["absolute"], AbsoluteLimits._body_mapping()) + self.absolute = AbsoluteLimits.existing(**absolute_body) + + rates_body = body["rate"] rates = [] - for rate in rate_body: - rates.append(RateLimits(rate)) + for rate_body in rates_body: + rate_body = self._transpose_component(rate_body, + RateLimit._body_mapping()) + rates.append(RateLimit(**rate_body)) + + self.rate = rates - self._attrs.update({"rate": rates}) - self._loaded = True return self diff --git a/openstack/compute/v2/server.py b/openstack/compute/v2/server.py index 5aecc8396..541034446 100644 --- a/openstack/compute/v2/server.py +++ b/openstack/compute/v2/server.py @@ -12,11 +12,11 @@ from openstack.compute import compute_service from openstack.compute.v2 import metadata -from openstack import resource +from openstack import resource2 from openstack import utils -class Server(resource.Resource, metadata.MetadataMixin): +class Server(resource2.Resource, metadata.MetadataMixin): resource_key = 'server' resources_key = 'servers' base_path = '/servers' @@ -24,89 +24,130 @@ class Server(resource.Resource, metadata.MetadataMixin): # capabilities allow_create = True - allow_retrieve = True + allow_get = True allow_update = True allow_delete = True allow_list = True - # Properties - access_ipv4 = resource.prop('accessIPv4') - access_ipv6 = resource.prop('accessIPv6') + _query_mapping = resource2.QueryParameters("image", "flavor", "name", + "status", "host", + changes_since="changes-since") + + #: A list of dictionaries holding links relevant to this server. + links = resource2.Body('links') + + access_ipv4 = resource2.Body('accessIPv4') + access_ipv6 = 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 = resource.prop('addresses', type=dict) + addresses = resource2.Body('addresses', type=dict) #: Timestamp of when the server was created. - created_at = resource.prop('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 = resource.prop('flavorRef') + flavor_id = resource2.Body('flavorRef') #: An ID representing the host of this server. - host_id = resource.prop('hostId') + host_id = resource2.Body('hostId') #: The image reference, as a ID or full URL, for the image to use for #: this server. - image_id = resource.prop('imageRef') - #: A list of dictionaries holding links relevant to this server. - links = resource.prop('links') + image_id = resource2.Body('imageRef') #: Metadata stored for this server. *Type: dict* - metadata = resource.prop('metadata', type=dict) - #: The name of this server. - name = resource.prop('name') + metadata = resource2.Body('metadata', type=dict) #: While the server is building, this value represents the percentage #: of completion. Once it is completed, it will be 100. *Type: int* - progress = resource.prop('progress', type=int) + progress = resource2.Body('progress', type=int) #: The ID of the project this server is associated with. - project_id = resource.prop('tenant_id') + project_id = resource2.Body('tenant_id') #: 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 = resource.prop('status') + status = resource2.Body('status') #: Timestamp of when this server was last updated. - updated_at = resource.prop('updated') - #: The user ID associated with this server. - user_id = resource.prop('user_id') + updated_at = resource2.Body('updated') + #: The ID of the owners of this server. + user_id = resource2.Body('user_id') + #: The name of an associated keypair + key_name = resource2.Body('key_name') + #: The disk configuration. Either AUTO or MANUAL. + disk_config = 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') + #: A list of applicable security groups. Each group contains keys for + #: description, name, id, and rules. + security_groups = resource2.Body('security_groups') + #: When a server is first created, it provides the administrator password. + admin_password = resource2.Body('adminPass') + #: 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_v2', type=dict) + #: The dictionary of data to send to the scheduler. + scheduler_hints = resource2.Body('os:scheduler_hints', type=dict) + #: 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) - @classmethod - def _get_create_body(cls, attrs): - body = {} - if 'scheduler_hints' in attrs: - hints = attrs.pop('scheduler_hints') - body['os:scheduler_hints'] = hints - body[cls.resource_key] = attrs - - return body - - def action(self, session, body): + def _action(self, session, body): """Preform server actions given the message body.""" - url = utils.urljoin(self.base_path, self.id, 'action') + # 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': ''} - session.post( + return session.post( url, endpoint_filter=self.service, json=body, headers=headers) def change_password(self, session, new_password): """Change the administrator password to the given password.""" body = {'changePassword': {'adminPass': new_password}} - return self.action(session, body) + self._action(session, body) def reboot(self, session, reboot_type): """Reboot server where reboot_type might be 'SOFT' or 'HARD'.""" body = {'reboot': {'type': reboot_type}} - return self.action(session, body) + self._action(session, body) - def rebuild(self, session, name, image_href, admin_password, + 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, - 'imageRef': image_href, + '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: @@ -115,23 +156,26 @@ class Server(resource.Resource, metadata.MetadataMixin): action['metadata'] = metadata if personality is not None: action['personality'] = personality + body = {'rebuild': action} - return self.action(session, body) + response = self._action(session, body) + self._translate_response(response) + return self def resize(self, session, flavor): """Resize server to flavor reference.""" body = {'resize': {'flavorRef': flavor}} - return self.action(session, body) + self._action(session, body) def confirm_resize(self, session): """Confirm the resize of the server.""" body = {'confirmResize': None} - return self.action(session, body) + self._action(session, body) def revert_resize(self, session): """Revert the resize of the server.""" body = {'revertResize': None} - return self.action(session, body) + self._action(session, body) def create_image(self, session, name, metadata=None): """Create image from server.""" @@ -139,16 +183,15 @@ class Server(resource.Resource, metadata.MetadataMixin): if metadata is not None: action['metadata'] = metadata body = {'createImage': action} - return self.action(session, body) + self._action(session, body) - def get_floating_ips(self): - """Get the floating ips associated with this server.""" - addresses = self.addresses[self.name] - result = [] - for address in addresses: - if address['OS-EXT-IPS:type'] == 'floating': - result.append(address['addr']) - return result + def add_security_group(self, session, security_group): + body = {"addSecurityGroup": {"name": security_group}} + self._action(session, body) + + def remove_security_group(self, session, security_group): + body = {"removeSecurityGroup": {"name": security_group}} + self._action(session, body) class ServerDetail(Server): @@ -156,7 +199,7 @@ class ServerDetail(Server): # capabilities allow_create = False - allow_retrieve = False + allow_get = False allow_update = False allow_delete = False allow_list = True diff --git a/openstack/compute/v2/server_group.py b/openstack/compute/v2/server_group.py index 3254af7f2..f57c8e58a 100644 --- a/openstack/compute/v2/server_group.py +++ b/openstack/compute/v2/server_group.py @@ -11,27 +11,29 @@ # under the License. from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class ServerGroup(resource.Resource): +class ServerGroup(resource2.Resource): resource_key = 'server_group' resources_key = 'server_groups' base_path = '/os-server-groups' service = compute_service.ComputeService() + _query_mapping = resource2.QueryParameters("all_projects") + # capabilities allow_create = True - allow_retrieve = True + allow_get = True allow_delete = True allow_list = True # Properties #: A name identifying the server group - name = resource.prop('name') + name = resource2.Body('name') #: The list of policies supported by the server group - policies = resource.prop('policies') + policies = resource2.Body('policies') #: The list of members in the server group - member_ids = resource.prop('members') + member_ids = resource2.Body('members') #: The metadata associated with the server group - metadata = resource.prop('metadata') + metadata = resource2.Body('metadata') diff --git a/openstack/compute/v2/server_interface.py b/openstack/compute/v2/server_interface.py index 66789a0bc..951f6e422 100644 --- a/openstack/compute/v2/server_interface.py +++ b/openstack/compute/v2/server_interface.py @@ -11,11 +11,10 @@ # under the License. from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 -class ServerInterface(resource.Resource): - id_attribute = 'port_id' +class ServerInterface(resource2.Resource): resource_key = 'interfaceAttachment' resources_key = 'interfaceAttachments' base_path = '/servers/%(server_id)s/os-interface' @@ -23,21 +22,20 @@ class ServerInterface(resource.Resource): # capabilities allow_create = True - allow_retrieve = True + allow_get = True allow_update = False allow_delete = True allow_list = True - # Properties #: Fixed IP addresses with subnet IDs. - fixed_ips = resource.prop('fixed_ips') + fixed_ips = resource2.Body('fixed_ips') #: The MAC address. - mac_addr = resource.prop('mac_addr') + mac_addr = resource2.Body('mac_addr') #: The network ID. - net_id = resource.prop('net_id') + net_id = resource2.Body('net_id') #: The ID of the port for which you want to create an interface. - port_id = resource.prop('port_id') + port_id = resource2.Body('port_id', alternate_id=True) #: The port state. - port_state = resource.prop('port_state') + port_state = resource2.Body('port_state') #: The ID for the server. - server_id = resource.prop('server_id') + server_id = resource2.URI('server_id') diff --git a/openstack/compute/v2/server_ip.py b/openstack/compute/v2/server_ip.py index c6e811d7e..bc90d645d 100644 --- a/openstack/compute/v2/server_ip.py +++ b/openstack/compute/v2/server_ip.py @@ -10,16 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. -import six - from openstack.compute import compute_service -from openstack import resource +from openstack import resource2 +from openstack import utils -class ServerIP(resource.Resource): - id_attribute = 'addr' - resource_key = 'server_ip' - resources_key = 'server_ips' +class ServerIP(resource2.Resource): + resources_key = 'addresses' base_path = '/servers/%(server_id)s/ips' service = compute_service.ComputeService() @@ -28,27 +25,30 @@ class ServerIP(resource.Resource): # Properties #: The IP address. The format of the address depends on :attr:`version` - addr = resource.prop('addr') + address = resource2.Body('addr') #: The network label, such as public or private. - network_label = resource.prop('network_label') + network_label = resource2.URI('network_label') #: The ID for the server. - server_id = resource.prop('server_id') + server_id = resource2.URI('server_id') # Version of the IP protocol. Currently either 4 or 6. - version = resource.prop('version') + version = resource2.Body('version') @classmethod - def list(cls, session, path_args=None, **params): - url = cls._get_url(path_args) - resp = session.get(url, endpoint_filter=cls.service, params=params) + def list(cls, session, paginated=False, server_id=None, + network_label=None, **params): + url = cls.base_path % {"server_id": server_id} + + if network_label is not None: + url = utils.urljoin(url, network_label) + + resp = session.get(url, endpoint_filter=cls.service) resp = resp.json() - ray = [] - for network_label, addresses in six.iteritems(resp['addresses']): + + if network_label is None: + resp = resp[cls.resources_key] + + for label, addresses in resp.items(): for address in addresses: - record = { - 'server_id': path_args['server_id'], - 'network_label': network_label, - 'version': address['version'], - 'addr': address['addr'], - } - ray.append(cls.existing(**record)) - return ray + yield cls.existing(network_label=label, + address=address["addr"], + version=address["version"]) diff --git a/openstack/tests/functional/compute/v2/test_image.py b/openstack/tests/functional/compute/v2/test_image.py index 79200bfa2..09069d697 100644 --- a/openstack/tests/functional/compute/v2/test_image.py +++ b/openstack/tests/functional/compute/v2/test_image.py @@ -46,12 +46,12 @@ class TestImage(base.BaseFunctionalTest): sot = self.conn.compute.get_image(image.id) self.assertEqual(image.id, sot.id) self.assertEqual(image.name, sot.name) - self.assertIn('links', image) - self.assertIn('minDisk', image) - self.assertIn('minRam', image) - self.assertIn('metadata', image) - self.assertIn('progress', image) - self.assertIn('status', image) + self.assertIsNotNone(image.links) + self.assertIsNotNone(image.min_disk) + self.assertIsNotNone(image.min_ram) + self.assertIsNotNone(image.metadata) + self.assertIsNotNone(image.progress) + self.assertIsNotNone(image.status) def test_image_metadata(self): image = self._get_non_test_image() diff --git a/openstack/tests/functional/compute/v2/test_keypair.py b/openstack/tests/functional/compute/v2/test_keypair.py index 4d070c795..3b2f2f63e 100644 --- a/openstack/tests/functional/compute/v2/test_keypair.py +++ b/openstack/tests/functional/compute/v2/test_keypair.py @@ -27,11 +27,12 @@ class TestKeypair(base.BaseFunctionalTest): sot = cls.conn.compute.create_keypair(name=cls.NAME) assert isinstance(sot, keypair.Keypair) cls.assertIs(cls.NAME, sot.name) + cls._keypair = sot cls.ID = sot.id @classmethod def tearDownClass(cls): - sot = cls.conn.compute.delete_keypair(cls.ID) + sot = cls.conn.compute.delete_keypair(cls._keypair) cls.assertIs(None, sot) def test_find(self): diff --git a/openstack/tests/functional/compute/v2/test_limits.py b/openstack/tests/functional/compute/v2/test_limits.py index c37e4fb91..8291b1c84 100644 --- a/openstack/tests/functional/compute/v2/test_limits.py +++ b/openstack/tests/functional/compute/v2/test_limits.py @@ -17,8 +17,8 @@ class TestLimits(base.BaseFunctionalTest): def test_limits(self): sot = self.conn.compute.get_limits() - self.assertIn('maxTotalInstances', sot.absolute) - self.assertIn('maxTotalRAMSize', sot.absolute) - self.assertIn('maxTotalKeypairs', sot.absolute) - self.assertIn('maxSecurityGroups', sot.absolute) - self.assertIn('maxSecurityGroupRules', sot.absolute) + self.assertIsNotNone('maxTotalInstances', sot.absolute) + self.assertIsNotNone('maxTotalRAMSize', sot.absolute) + self.assertIsNotNone('maxTotalKeypairs', sot.absolute) + self.assertIsNotNone('maxSecurityGroups', sot.absolute) + self.assertIsNotNone('maxSecurityGroupRules', sot.absolute) diff --git a/openstack/tests/functional/compute/v2/test_server.py b/openstack/tests/functional/compute/v2/test_server.py index a88dd0af0..795e8180d 100644 --- a/openstack/tests/functional/compute/v2/test_server.py +++ b/openstack/tests/functional/compute/v2/test_server.py @@ -35,12 +35,15 @@ class TestServer(base.BaseFunctionalTest): cls.network, cls.subnet = test_network.create_network(cls.conn, cls.NAME, cls.cidr) - if cls.network: - args = {'networks': [{"uuid": cls.network.id}]} - else: - args = {} + if not cls.network: + # We can't call TestCase.fail from within the setUpClass + # classmethod, but we need to raise some exception in order + # to get this setup to fail and thusly fail the entire class. + raise Exception("Unable to create network for TestServer") + sot = cls.conn.compute.create_server( - name=cls.NAME, flavor_id=flavor.id, image_id=image.id, **args) + name=cls.NAME, flavor_id=flavor.id, image_id=image.id, + networks=[{"uuid": cls.network.id}]) cls.conn.compute.wait_for_server(sot) assert isinstance(sot, server.Server) cls.assertIs(cls.NAME, sot.name) diff --git a/openstack/tests/unit/compute/v2/test_availability_zone.py b/openstack/tests/unit/compute/v2/test_availability_zone.py index 07ff4c7b9..4d4abe099 100644 --- a/openstack/tests/unit/compute/v2/test_availability_zone.py +++ b/openstack/tests/unit/compute/v2/test_availability_zone.py @@ -27,14 +27,20 @@ class TestAvailabilityZone(testtools.TestCase): def test_basic(self): sot = az.AvailabilityZone() - self.assertEqual('availability_zone', sot.resource_key) self.assertEqual('availabilityZoneInfo', sot.resources_key) self.assertEqual('/os-availability-zone', sot.base_path) self.assertTrue(sot.allow_list) self.assertEqual('compute', sot.service.service_type) + def test_basic_detail(self): + sot = az.AvailabilityZoneDetail() + self.assertEqual('availabilityZoneInfo', sot.resources_key) + self.assertEqual('/os-availability-zone/detail', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertEqual('compute', sot.service.service_type) + def test_make_basic(self): - sot = az.AvailabilityZone(BASIC_EXAMPLE) + sot = az.AvailabilityZone(**BASIC_EXAMPLE) self.assertEqual(BASIC_EXAMPLE['id'], sot.id) self.assertEqual(BASIC_EXAMPLE['zoneState'], sot.state) self.assertEqual(BASIC_EXAMPLE['hosts'], sot.hosts) diff --git a/openstack/tests/unit/compute/v2/test_extension.py b/openstack/tests/unit/compute/v2/test_extension.py index e94ca6fb1..8d59084b2 100644 --- a/openstack/tests/unit/compute/v2/test_extension.py +++ b/openstack/tests/unit/compute/v2/test_extension.py @@ -34,14 +34,13 @@ class TestExtension(testtools.TestCase): self.assertEqual('/extensions', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertFalse(sot.allow_create) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertTrue(sot.allow_list) def test_make_it(self): - sot = extension.Extension(EXAMPLE) - self.assertEqual(EXAMPLE['alias'], sot.id) + sot = extension.Extension(**EXAMPLE) self.assertEqual(EXAMPLE['alias'], sot.alias) self.assertEqual(EXAMPLE['description'], sot.description) self.assertEqual(EXAMPLE['links'], sot.links) diff --git a/openstack/tests/unit/compute/v2/test_flavor.py b/openstack/tests/unit/compute/v2/test_flavor.py index a06a865b8..90db8d2a1 100644 --- a/openstack/tests/unit/compute/v2/test_flavor.py +++ b/openstack/tests/unit/compute/v2/test_flavor.py @@ -39,13 +39,19 @@ class TestFlavor(testtools.TestCase): self.assertEqual('/flavors', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertTrue(sot.allow_create) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertTrue(sot.allow_update) self.assertTrue(sot.allow_delete) self.assertTrue(sot.allow_list) + self.assertDictEqual({"sort_key": "sort_key", + "sort_dir": "sort_dir", + "min_disk": "minDisk", + "min_ram": "minRam"}, + sot._query_mapping._mapping) + def test_make_basic(self): - sot = flavor.Flavor(BASIC_EXAMPLE) + sot = flavor.Flavor(**BASIC_EXAMPLE) self.assertEqual(BASIC_EXAMPLE['id'], sot.id) self.assertEqual(BASIC_EXAMPLE['links'], sot.links) self.assertEqual(BASIC_EXAMPLE['name'], sot.name) @@ -68,7 +74,7 @@ class TestFlavor(testtools.TestCase): self.assertEqual('/flavors/detail', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertFalse(sot.allow_create) - self.assertFalse(sot.allow_retrieve) + self.assertFalse(sot.allow_get) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertTrue(sot.allow_list) diff --git a/openstack/tests/unit/compute/v2/test_hypervisor.py b/openstack/tests/unit/compute/v2/test_hypervisor.py index 0a6ab696d..04829c081 100644 --- a/openstack/tests/unit/compute/v2/test_hypervisor.py +++ b/openstack/tests/unit/compute/v2/test_hypervisor.py @@ -15,10 +15,30 @@ import testtools from openstack.compute.v2 import hypervisor EXAMPLE = { - 'id': 'IDENTIFIER', - 'name': 'hypervisor_hostname', - 'state': 'up', - 'status': 'enabled', + "status": "enabled", + "service": { + "host": "fake-mini", + "disabled_reason": None, + "id": 6 + }, + "vcpus_used": 0, + "hypervisor_type": "QEMU", + "local_gb_used": 0, + "vcpus": 8, + "hypervisor_hostname": "fake-mini", + "memory_mb_used": 512, + "memory_mb": 7980, + "current_workload": 0, + "state": "up", + "host_ip": "23.253.248.171", + "cpu_info": "some cpu info", + "running_vms": 0, + "free_disk_gb": 157, + "hypervisor_version": 2000000, + "disk_available_least": 140, + "local_gb": 157, + "free_ram_mb": 7468, + "id": 1 } @@ -30,12 +50,29 @@ class TestHypervisor(testtools.TestCase): self.assertEqual('hypervisors', sot.resources_key) self.assertEqual('/os-hypervisors', sot.base_path) self.assertEqual('compute', sot.service.service_type) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertTrue(sot.allow_list) def test_make_it(self): - sot = hypervisor.Hypervisor(EXAMPLE) + sot = hypervisor.Hypervisor(**EXAMPLE) self.assertEqual(EXAMPLE['id'], sot.id) - self.assertEqual(EXAMPLE['name'], sot.name) + self.assertEqual(EXAMPLE['hypervisor_hostname'], sot.name) self.assertEqual(EXAMPLE['state'], sot.state) self.assertEqual(EXAMPLE['status'], sot.status) + self.assertEqual(EXAMPLE['service'], sot.service_details) + self.assertEqual(EXAMPLE['vcpus_used'], sot.vcpus_used) + self.assertEqual(EXAMPLE['hypervisor_type'], sot.hypervisor_type) + self.assertEqual(EXAMPLE['local_gb_used'], sot.local_disk_used) + self.assertEqual(EXAMPLE['vcpus'], sot.vcpus) + self.assertEqual(EXAMPLE['vcpus_used'], sot.vcpus_used) + self.assertEqual(EXAMPLE['memory_mb_used'], sot.memory_used) + self.assertEqual(EXAMPLE['memory_mb'], sot.memory_size) + self.assertEqual(EXAMPLE['current_workload'], sot.current_workload) + self.assertEqual(EXAMPLE['host_ip'], sot.host_ip) + self.assertEqual(EXAMPLE['cpu_info'], sot.cpu_info) + self.assertEqual(EXAMPLE['running_vms'], sot.running_vms) + self.assertEqual(EXAMPLE['free_disk_gb'], sot.local_disk_free) + self.assertEqual(EXAMPLE['hypervisor_version'], sot.hypervisor_version) + self.assertEqual(EXAMPLE['disk_available_least'], sot.disk_available) + self.assertEqual(EXAMPLE['local_gb'], sot.local_disk_size) + self.assertEqual(EXAMPLE['free_ram_mb'], sot.memory_free) diff --git a/openstack/tests/unit/compute/v2/test_image.py b/openstack/tests/unit/compute/v2/test_image.py index 9015e03c1..70e11a312 100644 --- a/openstack/tests/unit/compute/v2/test_image.py +++ b/openstack/tests/unit/compute/v2/test_image.py @@ -45,13 +45,22 @@ class TestImage(testtools.TestCase): self.assertEqual('/images', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertFalse(sot.allow_create) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertFalse(sot.allow_update) self.assertTrue(sot.allow_delete) self.assertTrue(sot.allow_list) + self.assertDictEqual({"server": "server", + "name": "name", + "status": "status", + "type": "type", + "min_disk": "minDisk", + "min_ram": "minRam", + "changes_since": "changes-since"}, + sot._query_mapping._mapping) + def test_make_basic(self): - sot = image.Image(BASIC_EXAMPLE) + sot = image.Image(**BASIC_EXAMPLE) self.assertEqual(BASIC_EXAMPLE['id'], sot.id) self.assertEqual(BASIC_EXAMPLE['links'], sot.links) self.assertEqual(BASIC_EXAMPLE['name'], sot.name) @@ -63,13 +72,13 @@ class TestImage(testtools.TestCase): self.assertEqual('/images/detail', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertFalse(sot.allow_create) - self.assertFalse(sot.allow_retrieve) + self.assertFalse(sot.allow_get) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertTrue(sot.allow_list) def test_make_detail(self): - sot = image.ImageDetail(DETAIL_EXAMPLE) + sot = image.ImageDetail(**DETAIL_EXAMPLE) self.assertEqual(DETAIL_EXAMPLE['created'], sot.created_at) self.assertEqual(DETAIL_EXAMPLE['id'], sot.id) self.assertEqual(DETAIL_EXAMPLE['links'], sot.links) diff --git a/openstack/tests/unit/compute/v2/test_keypair.py b/openstack/tests/unit/compute/v2/test_keypair.py index 47b8446c3..3fb3dbcbc 100644 --- a/openstack/tests/unit/compute/v2/test_keypair.py +++ b/openstack/tests/unit/compute/v2/test_keypair.py @@ -15,11 +15,10 @@ import testtools from openstack.compute.v2 import keypair EXAMPLE = { - 'keypair': { - 'fingerprint': '1', - 'name': '2', - 'public_key': '3', - } + 'fingerprint': '1', + 'name': '2', + 'public_key': '3', + 'private_key': '3', } @@ -32,13 +31,14 @@ class TestKeypair(testtools.TestCase): self.assertEqual('/os-keypairs', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertTrue(sot.allow_create) - self.assertTrue(sot.allow_retrieve) - self.assertTrue(sot.allow_update) + self.assertTrue(sot.allow_get) + self.assertFalse(sot.allow_update) self.assertTrue(sot.allow_delete) self.assertTrue(sot.allow_list) def test_make_it(self): - sot = keypair.Keypair(EXAMPLE) - self.assertEqual(EXAMPLE['keypair']['fingerprint'], sot.fingerprint) - self.assertEqual(EXAMPLE['keypair']['name'], sot.name) - self.assertEqual(EXAMPLE['keypair']['public_key'], sot.public_key) + sot = keypair.Keypair(**EXAMPLE) + self.assertEqual(EXAMPLE['fingerprint'], sot.fingerprint) + self.assertEqual(EXAMPLE['name'], sot.name) + self.assertEqual(EXAMPLE['public_key'], sot.public_key) + self.assertEqual(EXAMPLE['private_key'], sot.private_key) diff --git a/openstack/tests/unit/compute/v2/test_limits.py b/openstack/tests/unit/compute/v2/test_limits.py index fd4a9a6a6..e31349169 100644 --- a/openstack/tests/unit/compute/v2/test_limits.py +++ b/openstack/tests/unit/compute/v2/test_limits.py @@ -68,13 +68,13 @@ class TestAbsoluteLimits(testtools.TestCase): self.assertEqual("", sot.base_path) self.assertIsNone(sot.service) self.assertFalse(sot.allow_create) - self.assertFalse(sot.allow_retrieve) + self.assertFalse(sot.allow_get) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertFalse(sot.allow_list) def test_make_it(self): - sot = limits.AbsoluteLimits(ABSOLUTE_LIMITS) + sot = limits.AbsoluteLimits(**ABSOLUTE_LIMITS) self.assertEqual(ABSOLUTE_LIMITS["maxImageMeta"], sot.image_meta) self.assertEqual(ABSOLUTE_LIMITS["maxPersonality"], sot.personality) self.assertEqual(ABSOLUTE_LIMITS["maxPersonalitySize"], @@ -109,22 +109,22 @@ class TestAbsoluteLimits(testtools.TestCase): sot.total_cores_used) -class TestRateLimits(testtools.TestCase): +class TestRateLimit(testtools.TestCase): def test_basic(self): - sot = limits.RateLimits() + sot = limits.RateLimit() self.assertIsNone(sot.resource_key) self.assertIsNone(sot.resources_key) self.assertEqual("", sot.base_path) self.assertIsNone(sot.service) self.assertFalse(sot.allow_create) - self.assertFalse(sot.allow_retrieve) + self.assertFalse(sot.allow_get) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertFalse(sot.allow_list) def test_make_it(self): - sot = limits.RateLimits(RATE_LIMIT) + sot = limits.RateLimit(**RATE_LIMIT) self.assertEqual(RATE_LIMIT["regex"], sot.regex) self.assertEqual(RATE_LIMIT["uri"], sot.uri) self.assertEqual(RATE_LIMIT["limit"], sot.limits) @@ -137,19 +137,59 @@ class TestLimits(testtools.TestCase): self.assertEqual("limits", sot.resource_key) self.assertEqual("/limits", sot.base_path) self.assertEqual("compute", sot.service.service_type) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertFalse(sot.allow_create) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertFalse(sot.allow_list) - @mock.patch("openstack.resource.Resource.get_data_by_id") - def test_get(self, mock_get): - # Only return values under the limits key since that's our - # resource_key, which would be filtered out in get_data_by_id. - mock_get.return_value = LIMITS_BODY["limits"] + def test_get(self): + sess = mock.Mock() + resp = mock.Mock() + sess.get.return_value = resp + resp.json.return_value = LIMITS_BODY - sot = limits.Limits().get("fake session") + sot = limits.Limits().get(sess) - self.assertEqual(sot.absolute, limits.AbsoluteLimits(ABSOLUTE_LIMITS)) - self.assertEqual(sot.rate, [limits.RateLimits(RATE_LIMIT)]) + self.assertEqual(ABSOLUTE_LIMITS["maxImageMeta"], + sot.absolute.image_meta) + self.assertEqual(ABSOLUTE_LIMITS["maxPersonality"], + sot.absolute.personality) + self.assertEqual(ABSOLUTE_LIMITS["maxPersonalitySize"], + sot.absolute.personality_size) + self.assertEqual(ABSOLUTE_LIMITS["maxSecurityGroupRules"], + sot.absolute.security_group_rules) + self.assertEqual(ABSOLUTE_LIMITS["maxSecurityGroups"], + sot.absolute.security_groups) + self.assertEqual(ABSOLUTE_LIMITS["maxServerMeta"], + sot.absolute.server_meta) + self.assertEqual(ABSOLUTE_LIMITS["maxTotalCores"], + sot.absolute.total_cores) + self.assertEqual(ABSOLUTE_LIMITS["maxTotalFloatingIps"], + sot.absolute.floating_ips) + self.assertEqual(ABSOLUTE_LIMITS["maxTotalInstances"], + sot.absolute.instances) + self.assertEqual(ABSOLUTE_LIMITS["maxTotalKeypairs"], + sot.absolute.keypairs) + self.assertEqual(ABSOLUTE_LIMITS["maxTotalRAMSize"], + sot.absolute.total_ram) + self.assertEqual(ABSOLUTE_LIMITS["maxServerGroups"], + sot.absolute.server_groups) + self.assertEqual(ABSOLUTE_LIMITS["maxServerGroupMembers"], + sot.absolute.server_group_members) + self.assertEqual(ABSOLUTE_LIMITS["totalFloatingIpsUsed"], + sot.absolute.floating_ips_used) + self.assertEqual(ABSOLUTE_LIMITS["totalSecurityGroupsUsed"], + sot.absolute.security_groups_used) + self.assertEqual(ABSOLUTE_LIMITS["totalRAMUsed"], + sot.absolute.total_ram_used) + self.assertEqual(ABSOLUTE_LIMITS["totalInstancesUsed"], + sot.absolute.instances_used) + self.assertEqual(ABSOLUTE_LIMITS["totalServerGroupsUsed"], + sot.absolute.server_groups_used) + self.assertEqual(ABSOLUTE_LIMITS["totalCoresUsed"], + sot.absolute.total_cores_used) + + self.assertEqual(RATE_LIMIT["uri"], sot.rate[0].uri) + self.assertEqual(RATE_LIMIT["regex"], sot.rate[0].regex) + self.assertEqual(RATE_LIMIT["limit"], sot.rate[0].limits) diff --git a/openstack/tests/unit/compute/v2/test_metadata.py b/openstack/tests/unit/compute/v2/test_metadata.py index 0da6d6e45..458f931b1 100644 --- a/openstack/tests/unit/compute/v2/test_metadata.py +++ b/openstack/tests/unit/compute/v2/test_metadata.py @@ -32,12 +32,12 @@ class TestMetadata(testtools.TestCase): self.meta_result = {"meta": {"oh": "yeah"}} def test_get_all_metadata_Server(self): - self._test_get_all_metadata(server.Server({"id": IDENTIFIER})) + self._test_get_all_metadata(server.Server(id=IDENTIFIER)) def test_get_all_metadata_ServerDetail(self): # This is tested explicitly so we know ServerDetail items are # properly having /detail stripped out of their base_path. - self._test_get_all_metadata(server.ServerDetail({"id": IDENTIFIER})) + self._test_get_all_metadata(server.ServerDetail(id=IDENTIFIER)) def _test_get_all_metadata(self, sot): response = mock.Mock() @@ -58,7 +58,7 @@ class TestMetadata(testtools.TestCase): sess = mock.Mock() sess.post.return_value = response - sot = server.Server({"id": IDENTIFIER}) + sot = server.Server(id=IDENTIFIER) set_meta = {"lol": "rofl"} @@ -74,7 +74,7 @@ class TestMetadata(testtools.TestCase): sess = mock.Mock() sess.delete.return_value = None - sot = server.Server({"id": IDENTIFIER}) + sot = server.Server(id=IDENTIFIER) key = "hey" diff --git a/openstack/tests/unit/compute/v2/test_proxy.py b/openstack/tests/unit/compute/v2/test_proxy.py index 18cfb74f8..efa49edf1 100644 --- a/openstack/tests/unit/compute/v2/test_proxy.py +++ b/openstack/tests/unit/compute/v2/test_proxy.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import mock - from openstack.compute.v2 import _proxy from openstack.compute.v2 import availability_zone as az from openstack.compute.v2 import extension @@ -24,10 +22,10 @@ from openstack.compute.v2 import server from openstack.compute.v2 import server_group from openstack.compute.v2 import server_interface from openstack.compute.v2 import server_ip -from openstack.tests.unit import test_proxy_base +from openstack.tests.unit import test_proxy_base2 -class TestComputeProxy(test_proxy_base.TestProxyBase): +class TestComputeProxy(test_proxy_base2.TestProxyBase): def setUp(self): super(TestComputeProxy, self).setUp() self.proxy = _proxy.Proxy(self.session) @@ -36,8 +34,8 @@ class TestComputeProxy(test_proxy_base.TestProxyBase): self.verify_find(self.proxy.find_extension, extension.Extension) def test_extensions(self): - self.verify_list(self.proxy.extensions, extension.Extension, - paginated=False) + self.verify_list_no_kwargs(self.proxy.extensions, extension.Extension, + paginated=False) def test_flavor_create(self): self.verify_create(self.proxy.create_flavor, flavor.Flavor) @@ -109,11 +107,8 @@ class TestComputeProxy(test_proxy_base.TestProxyBase): self.verify_get(self.proxy.get_keypair, keypair.Keypair) def test_keypairs(self): - self.verify_list(self.proxy.keypairs, keypair.Keypair, - paginated=False) - - def test_keypair_update(self): - self.verify_update(self.proxy.update_keypair, keypair.Keypair) + self.verify_list_no_kwargs(self.proxy.keypairs, keypair.Keypair, + paginated=False) def test_limits_get(self): self.verify_get(self.proxy.get_limits, limits.Limits, value=[]) @@ -122,75 +117,89 @@ class TestComputeProxy(test_proxy_base.TestProxyBase): self.verify_create(self.proxy.create_server_interface, server_interface.ServerInterface, method_kwargs={"server": "test_id"}, - expected_kwargs={"path_args": { - "server_id": "test_id"}}) + expected_kwargs={"server_id": "test_id"}) def test_server_interface_delete(self): - test_interface = server_interface.ServerInterface.from_id( - "test_interface_id") - test_interface.server_id = "test_server_id" + self.proxy._get_uri_attribute = lambda *args: args[1] + + interface_id = "test_interface_id" + server_id = "test_server_id" + test_interface = server_interface.ServerInterface(id=interface_id) + test_interface.server_id = server_id # Case1: ServerInterface instance is provided as value - self._verify2("openstack.proxy.BaseProxy._delete", + self._verify2("openstack.proxy2.BaseProxy._delete", self.proxy.delete_server_interface, - method_args=[test_interface, "test_server_id"], - expected_args=[server_interface.ServerInterface, - test_interface], - expected_kwargs={"path_args": { - "server_id": "test_server_id"}, - "ignore_missing": True}) + method_args=[test_interface], + method_kwargs={"server": server_id}, + expected_args=[server_interface.ServerInterface], + expected_kwargs={"server_id": server_id, + "port_id": interface_id, + "ignore_missing": True}) # Case2: ServerInterface ID is provided as value - self._verify2("openstack.proxy.BaseProxy._delete", + self._verify2("openstack.proxy2.BaseProxy._delete", self.proxy.delete_server_interface, - method_args=["test_interface_id", "test_server_id"], - expected_args=[server_interface.ServerInterface, - "test_interface_id"], - expected_kwargs={"path_args": { - "server_id": "test_server_id"}, - "ignore_missing": True}) + method_args=[interface_id], + method_kwargs={"server": server_id}, + expected_args=[server_interface.ServerInterface], + expected_kwargs={"server_id": server_id, + "port_id": interface_id, + "ignore_missing": True}) def test_server_interface_delete_ignore(self): + self.proxy._get_uri_attribute = lambda *args: args[1] self.verify_delete(self.proxy.delete_server_interface, server_interface.ServerInterface, True, - {"server": "test_id"}, {"server_id": "test_id"}) + method_kwargs={"server": "test_id"}, + expected_args=[server_interface.ServerInterface], + expected_kwargs={"server_id": "test_id", + "port_id": "resource_or_id"}) def test_server_interface_get(self): - test_interface = server_interface.ServerInterface.from_id( - "test_interface_id") - test_interface.server_id = "test_server_id" + self.proxy._get_uri_attribute = lambda *args: args[1] + + interface_id = "test_interface_id" + server_id = "test_server_id" + test_interface = server_interface.ServerInterface(id=interface_id) + test_interface.server_id = server_id # Case1: ServerInterface instance is provided as value - self._verify2('openstack.proxy.BaseProxy._get', + self._verify2('openstack.proxy2.BaseProxy._get', self.proxy.get_server_interface, - method_args=[test_interface, "test_id"], - expected_args=[server_interface.ServerInterface, - test_interface], - expected_kwargs={"path_args": { - "server_id": "test_server_id"}}) + method_args=[test_interface], + method_kwargs={"server": server_id}, + expected_args=[server_interface.ServerInterface], + expected_kwargs={"port_id": interface_id, + "server_id": server_id}) # Case2: ServerInterface ID is provided as value - self._verify2('openstack.proxy.BaseProxy._get', + self._verify2('openstack.proxy2.BaseProxy._get', self.proxy.get_server_interface, - method_args=["test_interface_id", "test_server_id"], - expected_args=[server_interface.ServerInterface, - "test_interface_id"], - expected_kwargs={"path_args": { - "server_id": "test_server_id"}}) + method_args=[interface_id], + method_kwargs={"server": server_id}, + expected_args=[server_interface.ServerInterface], + expected_kwargs={"port_id": interface_id, + "server_id": server_id}) def test_server_interfaces(self): self.verify_list(self.proxy.server_interfaces, server_interface.ServerInterface, paginated=False, method_args=["test_id"], - expected_kwargs={"path_args": { - "server_id": "test_id"}}) + expected_kwargs={"server_id": "test_id"}) - def test_server_ip_find(self): - self.verify_find(self.proxy.find_server_ip, server_ip.ServerIP) - - def test_server_ips(self): + def test_server_ips_with_network_label(self): self.verify_list(self.proxy.server_ips, server_ip.ServerIP, - paginated=False) + paginated=False, method_args=["test_id"], + method_kwargs={"network_label": "test_label"}, + expected_kwargs={"server_id": "test_id", + "network_label": "test_label"}) + + def test_server_ips_without_network_label(self): + self.verify_list(self.proxy.server_ips, server_ip.ServerIP, + paginated=False, method_args=["test_id"], + expected_kwargs={"server_id": "test_id", + "network_label": None}) def test_server_create_attrs(self): self.verify_create(self.proxy.create_server, server.Server) @@ -212,7 +221,7 @@ class TestComputeProxy(test_proxy_base.TestProxyBase): paginated=True, method_kwargs={"details": True, "changes_since": 1, "image": 2}, - expected_kwargs={"changes-since": 1, "image": 2}) + expected_kwargs={"changes_since": 1, "image": 2}) def test_servers_not_detailed(self): self.verify_list(self.proxy.servers, server.Server, @@ -220,13 +229,13 @@ class TestComputeProxy(test_proxy_base.TestProxyBase): method_kwargs={"details": False, "changes_since": 1, "image": 2}, expected_kwargs={"paginated": True, - "changes-since": 1, "image": 2}) + "changes_since": 1, "image": 2}) def test_server_update(self): self.verify_update(self.proxy.update_server, server.Server) def test_server_wait_for(self): - value = server.Server(attrs={'id': '1234'}) + value = server.Server(id='1234') self.verify_wait_for_status( self.proxy.wait_for_server, method_args=[value], @@ -240,69 +249,61 @@ class TestComputeProxy(test_proxy_base.TestProxyBase): def test_server_confirm_resize(self): self._verify("openstack.compute.v2.server.Server.confirm_resize", - self.proxy.confirm_resize_server, + self.proxy.confirm_server_resize, method_args=["value"]) def test_server_revert_resize(self): self._verify("openstack.compute.v2.server.Server.revert_resize", - self.proxy.revert_resize_server, + self.proxy.revert_server_resize, method_args=["value"]) - @mock.patch.object(_proxy.Proxy, 'find_image') - def test_server_rebuild(self, mock_find_image): - image_obj = image.Image.from_id('test_image_id') - mock_find_image.side_effect = [image_obj, None] + def test_server_rebuild(self): + id = 'test_image_id' + image_obj = image.Image(id='test_image_id') - # Case1: image object is provided as image_ref + # Case1: image object is provided + # NOTE: Inside of Server.rebuild is where image_obj gets converted + # to an ID instead of object. self._verify('openstack.compute.v2.server.Server.rebuild', self.proxy.rebuild_server, - method_args=["value", image_obj, "test_server", - "test_pass"], - method_kwargs={"metadata": {"k1": "v1"}}, - expected_args=["test_server", "test_image_id", - "test_pass"], - expected_kwargs={"metadata": {"k1": "v1"}}) + method_args=["value", "test_server", "test_pass"], + method_kwargs={"metadata": {"k1": "v1"}, + "image": image_obj}, + expected_args=["test_server", "test_pass"], + expected_kwargs={"metadata": {"k1": "v1"}, + "image": image_obj}) - # Case2: image name or id is provided as image_ref + # Case2: image name or id is provided self._verify('openstack.compute.v2.server.Server.rebuild', self.proxy.rebuild_server, - method_args=["value", "test_image_name_or_id", - "test_server", "test_pass"], - method_kwargs={"metadata": {"k1": "v1"}}, - expected_args=["test_server", "test_image_id", - "test_pass"], - expected_kwargs={"metadata": {"k1": "v1"}}) - - # Case3: image URL is provided as image_ref - self._verify('openstack.compute.v2.server.Server.rebuild', - self.proxy.rebuild_server, - method_args=["value", "test_image_url", "test_server", - "test_pass"], - method_kwargs={"metadata": {"k1": "v1"}}, - expected_args=["test_server", "test_image_url", - "test_pass"], - expected_kwargs={"metadata": {"k1": "v1"}}) + method_args=["value", "test_server", "test_pass"], + method_kwargs={"metadata": {"k1": "v1"}, + "image": id}, + expected_args=["test_server", "test_pass"], + expected_kwargs={"metadata": {"k1": "v1"}, + "image": id}) def test_availability_zones(self): - self.verify_list(self.proxy.availability_zones, az.AvailabilityZone, - paginated=False) + self.verify_list_no_kwargs(self.proxy.availability_zones, + az.AvailabilityZone, + paginated=False) def test_get_all_server_metadata(self): self._verify2("openstack.compute.v2.server.Server.get_metadata", self.proxy.get_server_metadata, method_args=["value"], - method_result=server.Server.existing(id="value", - metadata={}), + method_result=server.Server(id="value", metadata={}), expected_args=[self.session], expected_result={}) def test_set_server_metadata(self): kwargs = {"a": "1", "b": "2"} + id = "an_id" self._verify2("openstack.compute.v2.server.Server.set_metadata", self.proxy.set_server_metadata, - method_args=["value"], + method_args=[id], method_kwargs=kwargs, - method_result=server.Server.existing(id="value", + method_result=server.Server.existing(id=id, metadata=kwargs), expected_args=[self.session], expected_kwargs=kwargs, @@ -340,8 +341,9 @@ class TestComputeProxy(test_proxy_base.TestProxyBase): paginated=False) def test_hypervisors(self): - self.verify_list(self.proxy.hypervisors, hypervisor.Hypervisor, - paginated=False) + self.verify_list_no_kwargs(self.proxy.hypervisors, + hypervisor.Hypervisor, + paginated=False) def test_find_hypervisor(self): self.verify_find(self.proxy.find_hypervisor, diff --git a/openstack/tests/unit/compute/v2/test_server.py b/openstack/tests/unit/compute/v2/test_server.py index 1e663a46a..7330d79b1 100644 --- a/openstack/tests/unit/compute/v2/test_server.py +++ b/openstack/tests/unit/compute/v2/test_server.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - import mock import testtools @@ -35,6 +33,21 @@ EXAMPLE = { 'status': '14', 'updated': '2015-03-09T12:15:57.233772', 'user_id': '16', + 'key_name': '17', + 'OS-DCF:diskConfig': '18', + 'OS-EXT-AZ:availability_zone': '19', + 'OS-EXT-STS:power_state': '20', + 'OS-EXT-STS:task_state': '21', + 'OS-EXT-STS:vm_state': '22', + 'os-extended-volumes:volumes_attached': '23', + 'OS-SRV-USG:launched_at': '2015-03-09T12:15:57.233772', + 'OS-SRV-USG:terminated_at': '2015-03-09T12:15:57.233772', + 'security_groups': '26', + 'adminPass': '27', + 'personality': '28', + 'block_device_mapping_v2': {'key': '29'}, + 'os:scheduler_hints': {'key': '30'}, + 'user_data': '31' } @@ -55,13 +68,21 @@ class TestServer(testtools.TestCase): self.assertEqual('/servers', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertTrue(sot.allow_create) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertTrue(sot.allow_update) self.assertTrue(sot.allow_delete) self.assertTrue(sot.allow_list) + self.assertDictEqual({"image": "image", + "flavor": "flavor", + "name": "name", + "status": "status", + "host": "host", + "changes_since": "changes-since"}, + sot._query_mapping._mapping) + def test_make_it(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) self.assertEqual(EXAMPLE['accessIPv4'], sot.access_ipv4) self.assertEqual(EXAMPLE['accessIPv6'], sot.access_ipv6) self.assertEqual(EXAMPLE['addresses'], sot.addresses) @@ -78,6 +99,25 @@ class TestServer(testtools.TestCase): self.assertEqual(EXAMPLE['status'], sot.status) self.assertEqual(EXAMPLE['updated'], sot.updated_at) self.assertEqual(EXAMPLE['user_id'], sot.user_id) + self.assertEqual(EXAMPLE['key_name'], sot.key_name) + self.assertEqual(EXAMPLE['OS-DCF:diskConfig'], sot.disk_config) + self.assertEqual(EXAMPLE['OS-EXT-AZ:availability_zone'], + sot.availability_zone) + self.assertEqual(EXAMPLE['OS-EXT-STS:power_state'], sot.power_state) + self.assertEqual(EXAMPLE['OS-EXT-STS:task_state'], sot.task_state) + self.assertEqual(EXAMPLE['OS-EXT-STS:vm_state'], sot.vm_state) + self.assertEqual(EXAMPLE['os-extended-volumes:volumes_attached'], + sot.attached_volumes) + self.assertEqual(EXAMPLE['OS-SRV-USG:launched_at'], sot.launched_at) + self.assertEqual(EXAMPLE['OS-SRV-USG:terminated_at'], + sot.terminated_at) + self.assertEqual(EXAMPLE['security_groups'], sot.security_groups) + self.assertEqual(EXAMPLE['adminPass'], sot.admin_password) + self.assertEqual(EXAMPLE['personality'], sot.personality) + self.assertEqual(EXAMPLE['block_device_mapping_v2'], + sot.block_device_mapping) + self.assertEqual(EXAMPLE['os:scheduler_hints'], sot.scheduler_hints) + self.assertEqual(EXAMPLE['user_data'], sot.user_data) def test_detail(self): sot = server.ServerDetail() @@ -86,22 +126,15 @@ class TestServer(testtools.TestCase): self.assertEqual('/servers/detail', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertFalse(sot.allow_create) - self.assertFalse(sot.allow_retrieve) + self.assertFalse(sot.allow_get) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertTrue(sot.allow_list) - def test_create_body(self): - params = copy.deepcopy(EXAMPLE) - params['scheduler_hints'] = {'group': 'GROUP1_ID'} - body = server.Server._get_create_body(params) - self.assertNotIn('scheduler_hints', body) - self.assertEqual({'group': 'GROUP1_ID'}, body['os:scheduler_hints']) - def test_change_passowrd(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) - self.assertEqual(self.resp.body, sot.change_password(self.sess, 'a')) + self.assertIsNone(sot.change_password(self.sess, 'a')) url = 'servers/IDENTIFIER/action' body = {"changePassword": {"adminPass": "a"}} @@ -110,9 +143,9 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_reboot(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) - self.assertEqual(self.resp.body, sot.reboot(self.sess, 'HARD')) + self.assertIsNone(sot.reboot(self.sess, 'HARD')) url = 'servers/IDENTIFIER/action' body = {"reboot": {"type": "HARD"}} @@ -121,21 +154,18 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_rebuild(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) + # Let the translate pass through, that portion is tested elsewhere + sot._translate_response = lambda arg: arg - self.assertEqual( - self.resp.body, - sot.rebuild( - self.sess, - name='noo', - image_href='http://image/1', - admin_password='seekr3t', - access_ipv4="12.34.56.78", - access_ipv6="fe80::100", - metadata={"meta var": "meta val"}, - personality=[{"path": "/etc/motd", "contents": "foo"}], - ) - ) + result = sot.rebuild(self.sess, name='noo', admin_password='seekr3t', + image='http://image/1', access_ipv4="12.34.56.78", + access_ipv6="fe80::100", + metadata={"meta var": "meta val"}, + personality=[{"path": "/etc/motd", + "contents": "foo"}]) + + self.assertIsInstance(result, server.Server) url = 'servers/IDENTIFIER/action' body = { @@ -147,6 +177,7 @@ class TestServer(testtools.TestCase): "accessIPv6": "fe80::100", "metadata": {"meta var": "meta val"}, "personality": [{"path": "/etc/motd", "contents": "foo"}], + "preserve_ephemeral": False } } headers = {'Accept': ''} @@ -154,17 +185,15 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_rebuild_minimal(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) + # Let the translate pass through, that portion is tested elsewhere + sot._translate_response = lambda arg: arg - self.assertEqual( - self.resp.body, - sot.rebuild( - self.sess, - name='nootoo', - image_href='http://image/2', - admin_password='seekr3two', - ) - ) + result = sot.rebuild(self.sess, name='nootoo', + admin_password='seekr3two', + image='http://image/2') + + self.assertIsInstance(result, server.Server) url = 'servers/IDENTIFIER/action' body = { @@ -172,6 +201,7 @@ class TestServer(testtools.TestCase): "name": "nootoo", "imageRef": "http://image/2", "adminPass": "seekr3two", + "preserve_ephemeral": False } } headers = {'Accept': ''} @@ -179,9 +209,9 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_resize(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) - self.assertEqual(self.resp.body, sot.resize(self.sess, '2')) + self.assertIsNone(sot.resize(self.sess, '2')) url = 'servers/IDENTIFIER/action' body = {"resize": {"flavorRef": "2"}} @@ -190,9 +220,9 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_confirm_resize(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) - self.assertEqual(self.resp.body, sot.confirm_resize(self.sess)) + self.assertIsNone(sot.confirm_resize(self.sess)) url = 'servers/IDENTIFIER/action' body = {"confirmResize": None} @@ -201,9 +231,9 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_revert_resize(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) - self.assertEqual(self.resp.body, sot.revert_resize(self.sess)) + self.assertIsNone(sot.revert_resize(self.sess)) url = 'servers/IDENTIFIER/action' body = {"revertResize": None} @@ -212,14 +242,11 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_create_image(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) name = 'noo' metadata = {'nu': 'image', 'created': 'today'} - self.assertEqual( - self.resp.body, - sot.create_image(self.sess, name, metadata) - ) + self.assertIsNone(sot.create_image(self.sess, name, metadata)) url = 'servers/IDENTIFIER/action' body = {"createImage": {'name': name, 'metadata': metadata}} @@ -228,13 +255,10 @@ class TestServer(testtools.TestCase): url, endpoint_filter=sot.service, json=body, headers=headers) def test_create_image_minimal(self): - sot = server.Server(EXAMPLE) + sot = server.Server(**EXAMPLE) name = 'noo' - self.assertEqual( - self.resp.body, - sot.create_image(self.sess, name) - ) + self.assertIsNone(self.resp.body, sot.create_image(self.sess, name)) url = 'servers/IDENTIFIER/action' body = {"createImage": {'name': name}} @@ -242,38 +266,24 @@ class TestServer(testtools.TestCase): self.sess.post.assert_called_with( url, endpoint_filter=dict(sot.service), json=body, headers=headers) - def test_get_ips(self): - name = "jenkins" - fixed = { - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:f9:58:b4", - "version": 4, - "addr": "10.3.3.8", - "OS-EXT-IPS:type": "fixed", - } - float1 = { - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:f9:58:b4", - "version": 4, - "addr": "15.125.3.1", - "OS-EXT-IPS:type": "floating", - } - float2 = { - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:f9:58:b4", - "version": 4, - "addr": "15.125.3.2", - "OS-EXT-IPS:type": "floating", - } + def test_add_security_group(self): + sot = server.Server(**EXAMPLE) - addresses = {name: [fixed]} - attrs = {'id': IDENTIFIER, 'name': name, 'addresses': addresses} - sot = server.Server(attrs=attrs) - self.assertEqual([], sot.get_floating_ips()) + self.assertIsNone(sot.add_security_group(self.sess, "group")) - addresses = {name: [fixed, float1, float2]} - attrs = {'id': IDENTIFIER, 'name': name, 'addresses': addresses} - sot = server.Server(attrs=attrs) - self.assertEqual(["15.125.3.1", "15.125.3.2"], sot.get_floating_ips()) + url = 'servers/IDENTIFIER/action' + body = {"addSecurityGroup": {"name": "group"}} + headers = {'Accept': ''} + self.sess.post.assert_called_with( + url, endpoint_filter=sot.service, json=body, headers=headers) - addresses = {name: [float1, fixed]} - attrs = {'id': IDENTIFIER, 'name': name, 'addresses': addresses} - sot = server.Server(attrs=attrs) - self.assertEqual(["15.125.3.1"], sot.get_floating_ips()) + def test_remove_security_group(self): + sot = server.Server(**EXAMPLE) + + self.assertIsNone(sot.remove_security_group(self.sess, "group")) + + url = 'servers/IDENTIFIER/action' + body = {"removeSecurityGroup": {"name": "group"}} + headers = {'Accept': ''} + self.sess.post.assert_called_with( + url, endpoint_filter=sot.service, json=body, headers=headers) diff --git a/openstack/tests/unit/compute/v2/test_server_group.py b/openstack/tests/unit/compute/v2/test_server_group.py index a6fc6f79a..ea6d983d5 100644 --- a/openstack/tests/unit/compute/v2/test_server_group.py +++ b/openstack/tests/unit/compute/v2/test_server_group.py @@ -32,13 +32,16 @@ class TestServerGroup(testtools.TestCase): self.assertEqual('/os-server-groups', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertTrue(sot.allow_create) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertFalse(sot.allow_update) self.assertTrue(sot.allow_delete) self.assertTrue(sot.allow_list) + self.assertDictEqual({"all_projects": "all_projects"}, + sot._query_mapping._mapping) + def test_make_it(self): - sot = server_group.ServerGroup(EXAMPLE) + sot = server_group.ServerGroup(**EXAMPLE) self.assertEqual(EXAMPLE['id'], sot.id) self.assertEqual(EXAMPLE['name'], sot.name) self.assertEqual(EXAMPLE['members'], sot.member_ids) diff --git a/openstack/tests/unit/compute/v2/test_server_interface.py b/openstack/tests/unit/compute/v2/test_server_interface.py index 79e10a19a..64467541a 100644 --- a/openstack/tests/unit/compute/v2/test_server_interface.py +++ b/openstack/tests/unit/compute/v2/test_server_interface.py @@ -39,15 +39,14 @@ class TestServerInterface(testtools.TestCase): self.assertEqual('/servers/%(server_id)s/os-interface', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertTrue(sot.allow_create) - self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_get) self.assertFalse(sot.allow_update) self.assertTrue(sot.allow_delete) self.assertTrue(sot.allow_list) def test_make_it(self): - sot = server_interface.ServerInterface(EXAMPLE) + sot = server_interface.ServerInterface(**EXAMPLE) self.assertEqual(EXAMPLE['fixed_ips'], sot.fixed_ips) - self.assertEqual(EXAMPLE['port_id'], sot.id) self.assertEqual(EXAMPLE['mac_addr'], sot.mac_addr) self.assertEqual(EXAMPLE['net_id'], sot.net_id) self.assertEqual(EXAMPLE['port_id'], sot.port_id) diff --git a/openstack/tests/unit/compute/v2/test_server_ip.py b/openstack/tests/unit/compute/v2/test_server_ip.py index 010bcd4d5..770b67a21 100644 --- a/openstack/tests/unit/compute/v2/test_server_ip.py +++ b/openstack/tests/unit/compute/v2/test_server_ip.py @@ -19,97 +19,82 @@ IDENTIFIER = 'IDENTIFIER' EXAMPLE = { 'addr': '1', 'network_label': '2', - 'server_id': '3', 'version': '4', } -BODY = { - "addresses": { - "public": [ - { - "version": 4, - "addr": "67.23.10.132" - }, - { - "version": 6, - "addr": "::babe:67.23.10.132" - }, - { - "version": 4, - "addr": "67.23.10.131" - }, - { - "version": 6, - "addr": "::babe:4317:0A83" - } - ], - "private": [ - { - "version": 4, - "addr": "10.176.42.16" - }, - { - "version": 6, - "addr": "::babe:10.176.42.16" - } - ] - } -} class TestServerIP(testtools.TestCase): def test_basic(self): sot = server_ip.ServerIP() - self.assertEqual('server_ip', sot.resource_key) - self.assertEqual('server_ips', sot.resources_key) + self.assertEqual('addresses', sot.resources_key) self.assertEqual('/servers/%(server_id)s/ips', sot.base_path) self.assertEqual('compute', sot.service.service_type) self.assertFalse(sot.allow_create) - self.assertFalse(sot.allow_retrieve) + self.assertFalse(sot.allow_get) self.assertFalse(sot.allow_update) self.assertFalse(sot.allow_delete) self.assertTrue(sot.allow_list) def test_make_it(self): - sot = server_ip.ServerIP(EXAMPLE) - self.assertEqual(EXAMPLE['addr'], sot.id) - self.assertEqual(EXAMPLE['addr'], sot.addr) + sot = server_ip.ServerIP(**EXAMPLE) + self.assertEqual(EXAMPLE['addr'], sot.address) self.assertEqual(EXAMPLE['network_label'], sot.network_label) - self.assertEqual(EXAMPLE['server_id'], sot.server_id) self.assertEqual(EXAMPLE['version'], sot.version) def test_list(self): sess = mock.Mock() resp = mock.Mock() - resp.json = mock.Mock(return_value=BODY) - sess.get = mock.Mock(return_value=resp) - path_args = {'server_id': IDENTIFIER} + sess.get.return_value = resp + resp.json.return_value = { + "addresses": {"label1": [{"version": 1, "addr": "a1"}, + {"version": 2, "addr": "a2"}], + "label2": [{"version": 3, "addr": "a3"}, + {"version": 4, "addr": "a4"}]}} - caps = server_ip.ServerIP.list(sess, path_args=path_args) + ips = list(server_ip.ServerIP.list(sess, server_id=IDENTIFIER)) - caps = sorted(caps, key=lambda cap: cap.id) - self.assertEqual(6, len(caps)) - self.assertEqual('10.176.42.16', caps[0].addr) - self.assertEqual('private', caps[0].network_label) - self.assertEqual(IDENTIFIER, caps[0].server_id) - self.assertEqual(4, caps[0].version) - self.assertEqual('67.23.10.131', caps[1].addr) - self.assertEqual('public', caps[1].network_label) - self.assertEqual(IDENTIFIER, caps[1].server_id) - self.assertEqual(4, caps[1].version) - self.assertEqual('67.23.10.132', caps[2].addr) - self.assertEqual('public', caps[2].network_label) - self.assertEqual(IDENTIFIER, caps[2].server_id) - self.assertEqual(4, caps[2].version) - self.assertEqual('::babe:10.176.42.16', caps[3].addr) - self.assertEqual('private', caps[3].network_label) - self.assertEqual(IDENTIFIER, caps[3].server_id) - self.assertEqual(6, caps[3].version) - self.assertEqual('::babe:4317:0A83', caps[4].addr) - self.assertEqual('public', caps[4].network_label) - self.assertEqual(IDENTIFIER, caps[4].server_id) - self.assertEqual(6, caps[4].version) - self.assertEqual('::babe:67.23.10.132', caps[5].addr) - self.assertEqual('public', caps[5].network_label) - self.assertEqual(IDENTIFIER, caps[5].server_id) - self.assertEqual(6, caps[5].version) + self.assertEqual(4, len(ips)) + ips = sorted(ips, key=lambda ip: ip.version) + + self.assertEqual(type(ips[0]), server_ip.ServerIP) + self.assertEqual(ips[0].network_label, "label1") + self.assertEqual(ips[0].address, "a1") + self.assertEqual(ips[0].version, 1) + self.assertEqual(type(ips[1]), server_ip.ServerIP) + self.assertEqual(ips[1].network_label, "label1") + self.assertEqual(ips[1].address, "a2") + self.assertEqual(ips[1].version, 2) + self.assertEqual(type(ips[2]), server_ip.ServerIP) + self.assertEqual(ips[2].network_label, "label2") + self.assertEqual(ips[2].address, "a3") + self.assertEqual(ips[2].version, 3) + self.assertEqual(type(ips[3]), server_ip.ServerIP) + self.assertEqual(ips[3].network_label, "label2") + self.assertEqual(ips[3].address, "a4") + self.assertEqual(ips[3].version, 4) + + def test_list_network_label(self): + label = "label1" + sess = mock.Mock() + resp = mock.Mock() + sess.get.return_value = resp + resp.json.return_value = {label: [{"version": 1, + "addr": "a1"}, + {"version": 2, + "addr": "a2"}]} + + ips = list(server_ip.ServerIP.list(sess, server_id=IDENTIFIER, + network_label=label)) + + self.assertEqual(2, len(ips)) + ips = sorted(ips, key=lambda ip: ip.version) + + self.assertEqual(type(ips[0]), server_ip.ServerIP) + self.assertEqual(ips[0].network_label, label) + self.assertEqual(ips[0].address, "a1") + self.assertEqual(ips[0].version, 1) + self.assertEqual(type(ips[1]), server_ip.ServerIP) + self.assertEqual(ips[1].network_label, label) + self.assertEqual(ips[1].address, "a2") + self.assertEqual(ips[1].version, 2)