From 457ac2962f9c8e6862b24c03addffc03573ef256 Mon Sep 17 00:00:00 2001 From: DanNiESh Date: Tue, 16 Apr 2024 16:11:41 -0400 Subject: [PATCH] Extend the client to use ESI SDK --- esileapclient/osc/plugin.py | 46 ++- esileapclient/osc/v2/__init__.py | 0 esileapclient/osc/v2/event.py | 80 ++++ esileapclient/osc/v2/lease.py | 275 ++++++++++++++ esileapclient/osc/v2/node.py | 59 +++ esileapclient/osc/v2/offer.py | 291 +++++++++++++++ esileapclient/tests/unit/osc/{v1 => }/base.py | 0 .../tests/unit/osc/{v1 => }/fakes.py | 0 esileapclient/tests/unit/osc/test_plugin.py | 28 +- .../tests/unit/osc/v1/mdc/test_mdc_lease.py | 4 +- .../tests/unit/osc/v1/mdc/test_mdc_offer.py | 4 +- esileapclient/tests/unit/osc/v1/test_event.py | 16 +- esileapclient/tests/unit/osc/v1/test_lease.py | 4 +- esileapclient/tests/unit/osc/v1/test_node.py | 16 +- esileapclient/tests/unit/osc/v1/test_offer.py | 4 +- esileapclient/tests/unit/osc/v2/__init__.py | 0 esileapclient/tests/unit/osc/v2/test_event.py | 81 +++++ esileapclient/tests/unit/osc/v2/test_lease.py | 342 ++++++++++++++++++ esileapclient/tests/unit/osc/v2/test_node.py | 121 +++++++ esileapclient/tests/unit/osc/v2/test_offer.py | 326 +++++++++++++++++ requirements.txt | 1 + setup.cfg | 14 + test-requirements.txt | 3 +- 23 files changed, 1676 insertions(+), 39 deletions(-) create mode 100644 esileapclient/osc/v2/__init__.py create mode 100644 esileapclient/osc/v2/event.py create mode 100644 esileapclient/osc/v2/lease.py create mode 100644 esileapclient/osc/v2/node.py create mode 100644 esileapclient/osc/v2/offer.py rename esileapclient/tests/unit/osc/{v1 => }/base.py (100%) rename esileapclient/tests/unit/osc/{v1 => }/fakes.py (100%) create mode 100644 esileapclient/tests/unit/osc/v2/__init__.py create mode 100644 esileapclient/tests/unit/osc/v2/test_event.py create mode 100644 esileapclient/tests/unit/osc/v2/test_lease.py create mode 100644 esileapclient/tests/unit/osc/v2/test_node.py create mode 100644 esileapclient/tests/unit/osc/v2/test_offer.py diff --git a/esileapclient/osc/plugin.py b/esileapclient/osc/plugin.py index 8393e1f..501287c 100644 --- a/esileapclient/osc/plugin.py +++ b/esileapclient/osc/plugin.py @@ -10,23 +10,25 @@ # License for the specific language governing permissions and limitations # under the License. +from esi import connection import logging - from osc_lib import utils +from openstackclient.i18n import _ -DEFAULT_API_VERSION = '1' +DEFAULT_API_VERSION = '2' # Required by the OSC plugin interface API_NAME = 'lease' -API_VERSION_OPTION = 'os_lease_api_version' +API_VERSION_OPTION = 'os_esileap_api_version' API_VERSIONS = { '1': 'esileapclient.v1.client.Client', + '2': 'esi.connection.ESIConnection', } OS_LEASE_API_LATEST = True -LAST_KNOWN_API_VERSION = '1' -LATEST_VERSION = '1' +LAST_KNOWN_API_VERSION = '2' +LATEST_VERSION = '2' LOG = logging.getLogger(__name__) @@ -42,20 +44,22 @@ def make_client(instance): """ requested_api_version = instance._api_version[API_NAME] + if requested_api_version == '1': + plugin_client = utils.get_client_class( + API_NAME, + requested_api_version, + API_VERSIONS) - plugin_client = utils.get_client_class( - API_NAME, - requested_api_version, - API_VERSIONS) - - client = plugin_client( - os_esileap_api_version=requested_api_version, - session=instance.session, - region_name=instance._region_name, - endpoint_override=None - ) + client = plugin_client( + os_esileap_api_version=requested_api_version, + session=instance.session, + region_name=instance._region_name, + endpoint_override=None + ) - return client + return client + elif requested_api_version == '2': + return connection.ESIConnection(config=instance._cli_options).lease def build_option_parser(parser): @@ -69,4 +73,12 @@ def build_option_parser(parser): :param argparse.ArgumentParser parser: The parser object that has been initialized by OpenStackShell. """ + parser.add_argument( + '--os-esileap-api-version', + metavar='', + default=DEFAULT_API_VERSION, + help=_('ESI-LEAP API version, default=%s') + % DEFAULT_API_VERSION, + ) + return parser diff --git a/esileapclient/osc/v2/__init__.py b/esileapclient/osc/v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/esileapclient/osc/v2/event.py b/esileapclient/osc/v2/event.py new file mode 100644 index 0000000..a5de74c --- /dev/null +++ b/esileapclient/osc/v2/event.py @@ -0,0 +1,80 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.command import command +from osc_lib import utils as oscutils + +from esileapclient.v1.event import Event as EVENT_RESOURCE + +LOG = logging.getLogger(__name__) + + +class ListEvent(command.Lister): + """List events.""" + + log = logging.getLogger(__name__ + ".ListEvent") + + def get_parser(self, prog_name): + parser = super(ListEvent, self).get_parser(prog_name) + + parser.add_argument( + '--project', + dest='project_id', + required=False, + help="Show all events associated with given project ID or name.") + parser.add_argument( + '--last-event-id', + dest='last_event_id', + required=False, + help="Show events after this event ID.") + parser.add_argument( + '--last-notification-time', + dest='last_event_time', + required=False, + help="Show events after this notification time.") + parser.add_argument( + '--event-type', + dest='event_type', + required=False, + help="Show events matching this event type.") + parser.add_argument( + '--resource-type', + dest='resource_type', + required=False, + help="Show events matching this resource type.") + parser.add_argument( + '--resource-uuid', + dest='resource_uuid', + required=False, + help="Show events matching this resource ID or name.") + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.lease + + filters = { + 'lessee_or_owner_id': parsed_args.project_id, + 'last_event_id': parsed_args.last_event_id, + 'last_event_time': parsed_args.last_event_time, + 'event_type': parsed_args.event_type, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + } + + data = list(client.events(**filters)) + columns = EVENT_RESOURCE.fields.keys() + labels = EVENT_RESOURCE.fields.values() + return (labels, + (oscutils.get_item_properties(s, columns) for s in data)) diff --git a/esileapclient/osc/v2/lease.py b/esileapclient/osc/v2/lease.py new file mode 100644 index 0000000..44b4ed3 --- /dev/null +++ b/esileapclient/osc/v2/lease.py @@ -0,0 +1,275 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +import json + +from osc_lib.command import command +from osc_lib import utils as oscutils + +from esileapclient.v1.lease import Lease as LEASE_RESOURCE + +LOG = logging.getLogger(__name__) + + +class CreateLease(command.ShowOne): + """Create a new lease.""" + + log = logging.getLogger(__name__ + ".CreateLease") + + def get_parser(self, prog_name): + parser = super(CreateLease, self).get_parser(prog_name) + + parser.add_argument( + "resource_uuid", + metavar="", + help="Resource UUID or name") + parser.add_argument( + 'project_id', + metavar="", + help="Project ID or name leasing the resource.") + parser.add_argument( + '--end-time', + dest='end_time', + required=False, + help="Time when the lease will expire.") + parser.add_argument( + '--name', + dest='name', + required=False, + help="Name of the lease being created. ") + parser.add_argument( + '--properties', + dest='properties', + required=False, + help="Record arbitrary key/value resource property " + "information. Pass in as a json object.") + parser.add_argument( + '--resource-type', + dest='resource_type', + required=False, + help="Use this resource type instead of the default.") + parser.add_argument( + '--start-time', + dest='start_time', + required=False, + help="Time when the resource will become usable.") + parser.add_argument( + '--purpose', + dest='purpose', + required=False, + help="Specify the purpose for leasing the node") + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.lease + + field_list = LEASE_RESOURCE._creation_attributes + fields = dict((k, v) for (k, v) in vars(parsed_args).items() + if k in field_list and v is not None) + + if 'properties' in fields: + fields['properties'] = json.loads(fields['properties']) + + lease = client.create_lease(**fields) + + data = dict([(f, getattr(lease, f, '')) for f in + LEASE_RESOURCE.fields]) + + return self.dict2columns(data) + + +class UpdateLease(command.ShowOne): + """Update a lease.""" + + log = logging.getLogger(__name__ + ".UpdateLease") + + def get_parser(self, prog_name): + parser = super(UpdateLease, self).get_parser(prog_name) + + parser.add_argument( + "uuid", + metavar="", + help="UUID of the lease") + parser.add_argument( + '--end-time', + dest='end_time', + required=False, + help="Time when the lease will expire.") + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.lease + + field_list = LEASE_RESOURCE._update_attributes + fields = dict((k, v) for (k, v) in vars(parsed_args).items() + if k in field_list and v is not None) + + lease = client.update_lease(parsed_args.uuid, **fields) + data = dict([(f, lease.get(f, '')) for f in + LEASE_RESOURCE.fields]) + return self.dict2columns(data) + + +class ListLease(command.Lister): + """List leases.""" + + log = logging.getLogger(__name__ + ".ListLease") + + def get_parser(self, prog_name): + parser = super(ListLease, self).get_parser(prog_name) + + parser.add_argument( + '--long', + default=False, + help="Show detailed information about the leases.", + action='store_true') + parser.add_argument( + '--all', + default=False, + help="Show all leases in the database. For admin use only.", + action='store_true') + parser.add_argument( + '--status', + dest='status', + required=False, + help="Show all leases with given status.") + parser.add_argument( + '--offer-uuid', + dest='offer_uuid', + required=False, + help="Show all leases with given offer_uuid.") + parser.add_argument( + '--time-range', + dest='time_range', + nargs=2, + required=False, + help="Show all leases with start and end times " + "which intersect with the given range." + "Must pass in two valid datetime strings." + "Example: --time-range 2020-06-30T00:00:00" + "2021-06-30T00:00:00") + parser.add_argument( + '--project', + dest='project_id', + required=False, + help="Show all leases owned by given project ID or name.") + parser.add_argument( + '--owner', + dest='owner_id', + required=False, + help="Show all leases relevant to an offer owner " + "by the owner's project ID or name.") + parser.add_argument( + '--resource-type', + dest='resource_type', + required=False, + help="Show all leases with given resource-type.") + parser.add_argument( + '--resource-uuid', + dest='resource_uuid', + required=False, + help="Show all leases with given resource-uuid.") + parser.add_argument( + '--resource-class', + dest='resource_class', + required=False, + help="Show all leases with given resource-class.") + parser.add_argument( + '--purpose', + dest='purpose', + required=False, + help="Show the purpose of leasing the node") + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + + filters = { + 'status': parsed_args.status, + 'offer_uuid': parsed_args.offer_uuid, + 'start_time': str(parsed_args.time_range[0]) if + parsed_args.time_range else None, + 'end_time': str(parsed_args.time_range[1]) if + parsed_args.time_range else None, + 'project_id': parsed_args.project_id, + 'owner_id': parsed_args.owner_id, + 'view': 'all' if parsed_args.all else None, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + 'resource_class': parsed_args.resource_class, + 'purpose': parsed_args.purpose + } + + data = list(client.leases(**filters)) + + if parsed_args.long: + columns = LEASE_RESOURCE.long_fields.keys() + labels = LEASE_RESOURCE.long_fields.values() + else: + columns = LEASE_RESOURCE.fields.keys() + labels = LEASE_RESOURCE.fields.values() + + return (labels, + (oscutils.get_item_properties(s, columns, formatters={ + 'resource_properties': oscutils.format_dict + }) for s in data)) + + +class ShowLease(command.ShowOne): + """Show lease details.""" + + log = logging.getLogger(__name__ + ".ShowLease") + + def get_parser(self, prog_name): + parser = super(ShowLease, self).get_parser(prog_name) + parser.add_argument( + "uuid", + metavar="", + help="UUID of the lease") + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + lease = client.get_lease(parsed_args.uuid) + + lease_info = {k: getattr(lease, k, '') for k in + LEASE_RESOURCE.detailed_fields} + resource_properties = lease_info['resource_properties'] + lease_info['resource_properties'] = oscutils.format_dict( + resource_properties) + + return zip(*sorted(lease_info.items())) + + +class DeleteLease(command.Command): + """Unregister lease""" + + log = logging.getLogger(__name__ + ".DeleteLease") + + def get_parser(self, prog_name): + parser = super(DeleteLease, self).get_parser(prog_name) + parser.add_argument( + "uuid", + metavar="", + help="Lease to delete (UUID)") + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + client.delete_lease(parsed_args.uuid) + print('Deleted lease %s' % parsed_args.uuid) diff --git a/esileapclient/osc/v2/node.py b/esileapclient/osc/v2/node.py new file mode 100644 index 0000000..614ada9 --- /dev/null +++ b/esileapclient/osc/v2/node.py @@ -0,0 +1,59 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.command import command +from osc_lib import utils as oscutils + +from esileapclient.v1.node import Node as NODE_RESOURCE + +LOG = logging.getLogger(__name__) + + +class ListNode(command.Lister): + """List nodes.""" + + log = logging.getLogger(__name__ + ".ListNode") + + def get_parser(self, prog_name): + parser = super(ListNode, self).get_parser(prog_name) + + parser.add_argument( + '--long', + default=False, + help="Show detailed information about the nodes.", + action='store_true') + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + + # No filters yet + filters = { + } + + data = list(client.nodes(**filters)) + + if parsed_args.long: + columns = NODE_RESOURCE.detailed_fields.keys() + labels = NODE_RESOURCE.detailed_fields.values() + else: + columns = NODE_RESOURCE.fields.keys() + labels = NODE_RESOURCE.fields.values() + + return (labels, + (oscutils.get_item_properties(s, columns, formatters={ + 'properties': oscutils.format_dict + }) for s in data)) diff --git a/esileapclient/osc/v2/offer.py b/esileapclient/osc/v2/offer.py new file mode 100644 index 0000000..0312ac1 --- /dev/null +++ b/esileapclient/osc/v2/offer.py @@ -0,0 +1,291 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +import json + +from osc_lib.command import command +from osc_lib import utils as oscutils + +from esileapclient.v1.lease import Lease as LEASE_RESOURCE +from esileapclient.v1.offer import Offer as OFFER_RESOURCE + +LOG = logging.getLogger(__name__) + + +class CreateOffer(command.ShowOne): + """Create a new offer.""" + + log = logging.getLogger(__name__ + ".CreateOffer") + + def get_parser(self, prog_name): + parser = super(CreateOffer, self).get_parser(prog_name) + + parser.add_argument( + "resource_uuid", + metavar="", + help="Resource UUID") + parser.add_argument( + '--end-time', + dest='end_time', + required=False, + help="Time when the offer will expire and no longer be " + "'available'.") + parser.add_argument( + '--lessee', + dest='lessee_id', + required=False, + help="Project subtree to which this offer will be limited.") + parser.add_argument( + '--name', + dest='name', + required=False, + help="Name of the offer being created. ") + parser.add_argument( + '--properties', + dest='properties', + required=False, + help="Record arbitrary key/value resource property " + "information. Pass in as a json object.") + parser.add_argument( + '--resource-type', + dest='resource_type', + required=False, + help="Use this resource type instead of the default.") + parser.add_argument( + '--start-time', + dest='start_time', + required=False, + help="Time when the offer will be made 'available'.") + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + + field_list = OFFER_RESOURCE._creation_attributes + + fields = dict((k, v) for (k, v) in vars(parsed_args).items() + if k in field_list and v is not None) + + if 'properties' in fields: + fields['properties'] = json.loads(fields['properties']) + + offer = client.create_offer(**fields) + + data = dict([(f, getattr(offer, f, '')) for f in + OFFER_RESOURCE.fields]) + + return self.dict2columns(data) + + +class ListOffer(command.Lister): + """List offers.""" + + log = logging.getLogger(__name__ + ".ListOffer") + + def get_parser(self, prog_name): + parser = super(ListOffer, self).get_parser(prog_name) + + parser.add_argument( + '--long', + default=False, + help="Show detailed information about the offers.", + action='store_true') + parser.add_argument( + '--status', + dest='status', + required=False, + help="Show all offers with given status.") + parser.add_argument( + '--time-range', + dest='time_range', + nargs=2, + required=False, + help="Show all offers with start and end times " + "which begin and end in the given range." + "Must pass in two valid datetime strings." + "Example: --time-range 2020-06-30T00:00:00" + "2021-06-30T00:00:00") + parser.add_argument( + '--availability-range', + dest='availability_range', + nargs=2, + required=False, + help="Show all offers with availabilities " + "which will have no conflicting leases within " + "the given range. Must pass in two valid datetime " + "strings." + "Example: --availability-range 2020-06-30T00:00:00" + "2021-06-30T00:00:00") + parser.add_argument( + '--project', + dest='project_id', + required=False, + help="Show all offers owned by given project ID or name.") + parser.add_argument( + '--resource-type', + dest='resource_type', + required=False, + help="Show all offers with given resource-type.") + parser.add_argument( + '--resource-uuid', + dest='resource_uuid', + required=False, + help="Show all offers with given resource-uuid.") + parser.add_argument( + '--resource-class', + dest='resource_class', + required=False, + help="Show all leases with given resource-class.") + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + + filters = { + 'status': parsed_args.status, + + 'start_time': str(parsed_args.time_range[0]) if + parsed_args.time_range else None, + 'end_time': str(parsed_args.time_range[1]) if + parsed_args.time_range else None, + + 'available_start_time': str(parsed_args.availability_range[0]) if + parsed_args.availability_range else None, + 'available_end_time': str(parsed_args.availability_range[1]) if + parsed_args.availability_range else None, + + 'project_id': parsed_args.project_id, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + 'resource_class': parsed_args.resource_class + } + + data = list(client.offers(**filters)) + + if parsed_args.long: + columns = OFFER_RESOURCE.long_fields.keys() + labels = OFFER_RESOURCE.long_fields.values() + else: + columns = OFFER_RESOURCE.fields.keys() + labels = OFFER_RESOURCE.fields.values() + + return (labels, + (oscutils.get_item_properties(s, columns, formatters={ + 'resource_properties': oscutils.format_dict + }) for s in data)) + + +class ShowOffer(command.ShowOne): + """Show offer details.""" + + log = logging.getLogger(__name__ + ".ShowOffer") + + def get_parser(self, prog_name): + parser = super(ShowOffer, self).get_parser(prog_name) + parser.add_argument( + "uuid", + metavar="", + help="UUID of the offer") + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + + offer = client.get_offer(parsed_args.uuid) + + offer_info = {k: getattr(offer, k, '') for k in + OFFER_RESOURCE.detailed_fields} + resource_properties = offer_info['resource_properties'] + offer_info['resource_properties'] = oscutils.format_dict( + resource_properties) + + return zip(*sorted(offer_info.items())) + + +class DeleteOffer(command.Command): + """Unregister offer""" + + log = logging.getLogger(__name__ + ".DeleteOffer") + + def get_parser(self, prog_name): + parser = super(DeleteOffer, self).get_parser(prog_name) + parser.add_argument( + "uuid", + metavar="", + help="Offer to delete (UUID)") + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + client.delete_offer(parsed_args.uuid) + print('Deleted offer %s' % parsed_args.uuid) + + +class ClaimOffer(command.ShowOne): + """Claim an offer.""" + + log = logging.getLogger(__name__ + ".ClaimOffer") + + def get_parser(self, prog_name): + parser = super(ClaimOffer, self).get_parser(prog_name) + + parser.add_argument( + "offer_uuid", + metavar="", + help="Offer UUID") + parser.add_argument( + '--end-time', + dest='end_time', + required=False, + help="Time when the offer will expire and no longer be " + "'available'.") + parser.add_argument( + '--start-time', + dest='start_time', + required=False, + help="Time when the offer will be made 'available'.") + parser.add_argument( + '--properties', + dest='properties', + required=False, + help="Record arbitrary key/value resource property " + "information. Pass in as a json object.") + + return parser + + def take_action(self, parsed_args): + + client = self.app.client_manager.lease + + field_list = LEASE_RESOURCE._creation_attributes + + fields = dict((k, v) for (k, v) in vars(parsed_args).items() + if k in field_list and v is not None) + + if 'properties' in fields: + fields['properties'] = json.loads(fields['properties']) + + lease = client.claim_offer(parsed_args.offer_uuid, **fields) + + data = dict([(f, lease.get(f, '')) for f in + LEASE_RESOURCE.fields]) + + return self.dict2columns(data) diff --git a/esileapclient/tests/unit/osc/v1/base.py b/esileapclient/tests/unit/osc/base.py similarity index 100% rename from esileapclient/tests/unit/osc/v1/base.py rename to esileapclient/tests/unit/osc/base.py diff --git a/esileapclient/tests/unit/osc/v1/fakes.py b/esileapclient/tests/unit/osc/fakes.py similarity index 100% rename from esileapclient/tests/unit/osc/v1/fakes.py rename to esileapclient/tests/unit/osc/fakes.py diff --git a/esileapclient/tests/unit/osc/test_plugin.py b/esileapclient/tests/unit/osc/test_plugin.py index ef7ad9a..61deac2 100644 --- a/esileapclient/tests/unit/osc/test_plugin.py +++ b/esileapclient/tests/unit/osc/test_plugin.py @@ -16,6 +16,8 @@ from esileapclient.osc import plugin from esileapclient.v1 import client +from esi import connection + API_VERSION = '1' @@ -27,6 +29,7 @@ def __init__(self): self._region_name = 'RegionOne' self.session = 'fake session' self._api_version = {'lease': API_VERSION} + self._cli_options = None class MakeClientTest(testtools.TestCase): @@ -41,16 +44,14 @@ def test_make_client_explicit_version(self, mock_client): region_name=instance._region_name, endpoint_override=None) - @mock.patch.object(client, 'Client') - def test_make_client_latest(self, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_make_client_latest(self, mock_conn): instance = FakeClientManager() + mock_conn.return_value.lease = mock.Mock() instance._api_version = {'lease': plugin.LATEST_VERSION} - plugin.make_client(instance) - mock_client.assert_called_once_with( - os_esileap_api_version=plugin.LATEST_VERSION, - session=instance.session, - region_name=instance._region_name, - endpoint_override=None) + lease = plugin.make_client(instance) + mock_conn.assert_called_once_with(config=None) + self.assertEqual(lease, mock_conn.return_value.lease) @mock.patch.object(client, 'Client') def test_make_client_v1(self, mock_client): @@ -58,7 +59,16 @@ def test_make_client_v1(self, mock_client): instance._api_version = {'lease': '1'} plugin.make_client(instance) mock_client.assert_called_once_with( - os_esileap_api_version=plugin.LATEST_VERSION, + os_esileap_api_version='1', session=instance.session, region_name=instance._region_name, endpoint_override=None) + + @mock.patch.object(connection, 'ESIConnection') + def test_make_client_v2(self, mock_conn): + instance = FakeClientManager() + mock_conn.return_value.lease = mock.Mock() + instance._api_version = {'lease': '2'} + lease = plugin.make_client(instance) + mock_conn.assert_called_once_with(config=None) + self.assertEqual(lease, mock_conn.return_value.lease) diff --git a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py index 9665cc1..9dd050f 100644 --- a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py +++ b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py @@ -14,8 +14,8 @@ import mock from esileapclient.osc.v1.mdc import mdc_lease -from esileapclient.tests.unit.osc.v1 import base -from esileapclient.tests.unit.osc.v1 import fakes +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes class TestMDCLease(base.TestESILeapCommand): diff --git a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py index f0734ba..9b818cf 100644 --- a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py +++ b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py @@ -17,8 +17,8 @@ from osc_lib import exceptions from esileapclient.osc.v1.mdc import mdc_offer -from esileapclient.tests.unit.osc.v1 import base -from esileapclient.tests.unit.osc.v1 import fakes +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes class TestMDCOffer(base.TestESILeapCommand): diff --git a/esileapclient/tests/unit/osc/v1/test_event.py b/esileapclient/tests/unit/osc/v1/test_event.py index 66cbd84..d95b6ab 100644 --- a/esileapclient/tests/unit/osc/v1/test_event.py +++ b/esileapclient/tests/unit/osc/v1/test_event.py @@ -1,8 +1,20 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import copy from esileapclient.osc.v1 import event -from esileapclient.tests.unit.osc.v1 import base -from esileapclient.tests.unit.osc.v1 import fakes +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes class TestEvent(base.TestESILeapCommand): diff --git a/esileapclient/tests/unit/osc/v1/test_lease.py b/esileapclient/tests/unit/osc/v1/test_lease.py index 70d2055..e43cef0 100644 --- a/esileapclient/tests/unit/osc/v1/test_lease.py +++ b/esileapclient/tests/unit/osc/v1/test_lease.py @@ -15,8 +15,8 @@ from osc_lib.tests import utils as osctestutils from esileapclient.osc.v1 import lease -from esileapclient.tests.unit.osc.v1 import base -from esileapclient.tests.unit.osc.v1 import fakes +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes class TestLease(base.TestESILeapCommand): diff --git a/esileapclient/tests/unit/osc/v1/test_node.py b/esileapclient/tests/unit/osc/v1/test_node.py index aacf6ee..ecbaf9a 100644 --- a/esileapclient/tests/unit/osc/v1/test_node.py +++ b/esileapclient/tests/unit/osc/v1/test_node.py @@ -1,8 +1,20 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import copy from esileapclient.osc.v1 import node -from esileapclient.tests.unit.osc.v1 import base -from esileapclient.tests.unit.osc.v1 import fakes +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes class TestNode(base.TestESILeapCommand): diff --git a/esileapclient/tests/unit/osc/v1/test_offer.py b/esileapclient/tests/unit/osc/v1/test_offer.py index 0e86f44..25a0af0 100644 --- a/esileapclient/tests/unit/osc/v1/test_offer.py +++ b/esileapclient/tests/unit/osc/v1/test_offer.py @@ -15,8 +15,8 @@ from osc_lib.tests import utils as osctestutils from esileapclient.osc.v1 import offer -from esileapclient.tests.unit.osc.v1 import base -from esileapclient.tests.unit.osc.v1 import fakes +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes class TestOffer(base.TestESILeapCommand): diff --git a/esileapclient/tests/unit/osc/v2/__init__.py b/esileapclient/tests/unit/osc/v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/esileapclient/tests/unit/osc/v2/test_event.py b/esileapclient/tests/unit/osc/v2/test_event.py new file mode 100644 index 0000000..66e7476 --- /dev/null +++ b/esileapclient/tests/unit/osc/v2/test_event.py @@ -0,0 +1,81 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy + +from esileapclient.osc.v2 import event +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes + + +class TestEvent(base.TestESILeapCommand): + + def setUp(self): + super(TestEvent, self).setUp() + + self.client_mock = self.app.client_manager.lease + self.client_mock.reset_mock() + + +class TestEventList(TestEvent): + + def setUp(self): + super(TestEventList, self).setUp() + + self.client_mock.events.return_value = [ + base.FakeResource(copy.deepcopy(fakes.EVENT)) + ] + self.cmd = event.ListEvent(self.app, None) + + def test_event_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + filters = { + 'lessee_or_owner_id': parsed_args.project_id, + 'last_event_id': parsed_args.last_event_id, + 'last_event_time': parsed_args.last_event_time, + 'event_type': parsed_args.event_type, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + } + + self.client_mock.events.assert_called_with(**filters) + + collist = [ + "ID", + "Event Type", + "Event Time", + "Object Type", + "Object UUID", + "Resource Type", + "Resource UUID", + "Lessee ID", + "Owner ID", + ] + + self.assertEqual(collist, list(columns)) + + datalist = ((fakes.event_id, + fakes.event_type, + fakes.event_time, + fakes.object_type, + fakes.lease_uuid, + fakes.lease_resource_type, + fakes.lease_resource_uuid, + fakes.lease_project_id, + fakes.lease_owner_id, + ),) + self.assertEqual(datalist, tuple(data)) diff --git a/esileapclient/tests/unit/osc/v2/test_lease.py b/esileapclient/tests/unit/osc/v2/test_lease.py new file mode 100644 index 0000000..f017fd0 --- /dev/null +++ b/esileapclient/tests/unit/osc/v2/test_lease.py @@ -0,0 +1,342 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import json +from osc_lib.tests import utils as osctestutils + +from esileapclient.osc.v2 import lease +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes + + +class TestLease(base.TestESILeapCommand): + + def setUp(self): + super(TestLease, self).setUp() + + self.client_mock = self.app.client_manager.lease + self.client_mock.reset_mock() + + +class TestCreateLease(TestLease): + + def setUp(self): + super(TestCreateLease, self).setUp() + + self.client_mock.create_lease.return_value = ( + base.FakeResource(copy.deepcopy(fakes.LEASE)) + ) + + # Get the command object to test + self.cmd = lease.CreateLease(self.app, None) + + def test_lease_create(self): + + arglist = [ + fakes.lease_resource_uuid, + fakes.lease_project_id, + '--end-time', fakes.lease_end_time, + '--name', fakes.lease_name, + '--properties', fakes.lease_properties, + '--resource-type', fakes.lease_resource_type, + '--start-time', fakes.lease_start_time, + '--purpose', fakes.lease_purpose, + ] + + verifylist = [ + ('end_time', fakes.lease_end_time), + ('name', fakes.lease_name), + ('project_id', fakes.lease_project_id), + ('properties', fakes.lease_properties), + ('resource_type', fakes.lease_resource_type), + ('resource_uuid', fakes.lease_resource_uuid), + ('start_time', fakes.lease_start_time), + ('purpose', fakes.lease_purpose), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + args = { + 'resource_type': fakes.lease_resource_type, + 'resource_uuid': fakes.lease_resource_uuid, + 'project_id': fakes.lease_project_id, + 'end_time': fakes.lease_end_time, + 'name': fakes.lease_name, + 'properties': json.loads(fakes.lease_properties), + 'start_time': fakes.lease_start_time, + 'purpose': fakes.lease_purpose, + } + + self.client_mock.create_lease.assert_called_once_with(**args) + + +class TestUpdateLease(TestLease): + + def setUp(self): + super(TestUpdateLease, self).setUp() + lease_return = base.FakeResource(copy.deepcopy(fakes.LEASE)) + self.client_mock.update_lease.return_value = ( + dict(lease_return.__dict__) + ) + + # Get the command object to test + self.cmd = lease.UpdateLease(self.app, None) + + def test_lease_update(self): + + arglist = [ + fakes.lease_uuid, + '--end-time', fakes.lease_end_time, + ] + + verifylist = [ + ('uuid', fakes.lease_uuid), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.client_mock.update_lease.assert_called_once_with( + fakes.lease_uuid, end_time=fakes.lease_end_time) + + def test_update_show_no_id(self): + arglist = [] + verifylist = [] + self.assertRaises(osctestutils.ParserException, + self.check_parser, + self.cmd, arglist, verifylist) + + +class TestLeaseList(TestLease): + + def setUp(self): + super(TestLeaseList, self).setUp() + + self.client_mock.leases.return_value = [ + base.FakeResource(copy.deepcopy(fakes.LEASE)) + ] + self.cmd = lease.ListLease(self.app, None) + + def test_lease_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + filters = { + 'status': parsed_args.status, + 'offer_uuid': parsed_args.offer_uuid, + 'start_time': str(parsed_args.time_range[0]) if + parsed_args.time_range else None, + 'end_time': str(parsed_args.time_range[1]) if + parsed_args.time_range else None, + 'project_id': parsed_args.project_id, + 'owner_id': parsed_args.owner_id, + 'view': 'all' if parsed_args.all else None, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + 'resource_class': parsed_args.resource_class, + 'purpose': parsed_args.purpose + } + + self.client_mock.leases.assert_called_with(**filters) + + collist = [ + "UUID", + "Resource", + "Resource Class", + "Project", + "Start Time", + "End Time", + "Offer UUID", + "Status", + "Purpose", + ] + + self.assertEqual(collist, list(columns)) + + datalist = ((fakes.lease_uuid, + fakes.lease_resource, + fakes.lease_resource_class, + fakes.lease_project, + fakes.lease_start_time, + fakes.lease_end_time, + fakes.offer_uuid, + fakes.lease_status, + fakes.lease_purpose + ),) + self.assertEqual(datalist, tuple(data)) + + def test_lease_list_long(self): + arglist = ['--long'] + verifylist = [('long', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + filters = { + 'status': parsed_args.status, + 'offer_uuid': parsed_args.offer_uuid, + 'start_time': str(parsed_args.time_range[0]) if + parsed_args.time_range else None, + 'end_time': str(parsed_args.time_range[1]) if + parsed_args.time_range else None, + 'project_id': parsed_args.project_id, + 'owner_id': parsed_args.owner_id, + 'view': 'all' if parsed_args.all else None, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + 'resource_class': parsed_args.resource_class, + 'purpose': parsed_args.purpose + } + + self.client_mock.leases.assert_called_with(**filters) + + long_collist = [ + 'UUID', + 'Resource', + 'Resource Class', + 'Resource Properties', + 'Project', + 'Start Time', + 'End Time', + 'Expire Time', + 'Fulfill Time', + 'Offer UUID', + 'Owner', + 'Parent Lease UUID', + 'Status', + 'Purpose' + ] + + self.assertEqual(long_collist, list(columns)) + + datalist = ((fakes.lease_uuid, + fakes.lease_resource, + fakes.lease_resource_class, + fakes.formatted_node_properties, + fakes.lease_project, + fakes.lease_start_time, + fakes.lease_end_time, + fakes.lease_expire_time, + fakes.lease_fulfill_time, + fakes.offer_uuid, + fakes.lease_owner, + fakes.parent_lease_uuid, + fakes.lease_status, + fakes.lease_purpose, + ),) + self.assertEqual(datalist, tuple(data)) + + +class TestLeaseShow(TestLease): + def setUp(self): + super(TestLeaseShow, self).setUp() + + self.client_mock.get_lease.return_value = \ + base.FakeResource(copy.deepcopy(fakes.LEASE)) + + self.cmd = lease.ShowLease(self.app, None) + + def test_lease_show(self): + arglist = [fakes.lease_uuid] + verifylist = [('uuid', fakes.lease_uuid)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.client_mock.get_lease.assert_called_once_with( + fakes.lease_uuid) + + collist = ( + "end_time", + "expire_time", + "fulfill_time", + "name", + "offer_uuid", + "owner", + "owner_id", + "parent_lease_uuid", + "project", + "project_id", + "properties", + "purpose", + "resource", + "resource_class", + "resource_properties", + "resource_type", + "resource_uuid", + "start_time", + "status", + "uuid" + ) + + self.assertEqual(collist, columns) + + datalist = (fakes.lease_end_time, + fakes.lease_expire_time, + fakes.lease_fulfill_time, + fakes.lease_name, + fakes.offer_uuid, + fakes.lease_owner, + fakes.lease_owner_id, + fakes.parent_lease_uuid, + fakes.lease_project, + fakes.lease_project_id, + json.loads(fakes.lease_properties), + fakes.lease_purpose, + fakes.lease_resource, + fakes.lease_resource_class, + fakes.formatted_node_properties, + fakes.lease_resource_type, + fakes.lease_resource_uuid, + fakes.lease_start_time, + fakes.lease_status, + fakes.lease_uuid + ) + self.assertEqual(datalist, tuple(data)) + + def test_lease_show_no_id(self): + arglist = [] + verifylist = [] + self.assertRaises(osctestutils.ParserException, + self.check_parser, + self.cmd, arglist, verifylist) + + +class TestLeaseDelete(TestLease): + def setUp(self): + super(TestLeaseDelete, self).setUp() + + self.cmd = lease.DeleteLease(self.app, None) + + def test_lease_delete(self): + arglist = [fakes.lease_uuid] + verifylist = [('uuid', fakes.lease_uuid)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.client_mock.delete_lease.assert_called_once_with( + fakes.lease_uuid) + + def test_lease_delete_no_id(self): + arglist = [] + verifylist = [] + self.assertRaises(osctestutils.ParserException, + self.check_parser, + self.cmd, arglist, verifylist) diff --git a/esileapclient/tests/unit/osc/v2/test_node.py b/esileapclient/tests/unit/osc/v2/test_node.py new file mode 100644 index 0000000..faa34fb --- /dev/null +++ b/esileapclient/tests/unit/osc/v2/test_node.py @@ -0,0 +1,121 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy + +from esileapclient.osc.v2 import node +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes + + +class TestNode(base.TestESILeapCommand): + + def setUp(self): + super(TestNode, self).setUp() + + self.client_mock = self.app.client_manager.lease + self.client_mock.reset_mock() + + +class TestNodeList(TestNode): + + def setUp(self): + super(TestNodeList, self).setUp() + + self.client_mock.nodes.return_value = [ + base.FakeResource(copy.deepcopy(fakes.NODE)) + ] + self.cmd = node.ListNode(self.app, None) + + def test_node_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + filters = { + } + + self.client_mock.nodes.assert_called_with(**filters) + + collist = [ + "Name", + "Owner", + "Lessee", + "Resource Class", + "Provision State", + "Maintenance", + "Offer UUID", + "Lease UUID", + ] + + self.assertEqual(collist, list(columns)) + + datalist = ((fakes.node_name, + fakes.node_owner, + '', fakes.lease_resource_class, + '', '', '', '' + ),) + self.assertEqual(datalist, tuple(data)) + + def test_node_list_long(self): + arglist = ['--long'] + verifylist = [('long', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + filters = { + } + + self.client_mock.nodes.assert_called_with(**filters) + + long_collist = [ + "UUID", + "Name", + "Owner", + "Lessee", + "Provision State", + "Maintenance", + "Offer UUID", + "Lease UUID", + "Future Offers", + "Future Leases" + ] + long_collist = [ + 'UUID', + 'Name', + 'Owner', + 'Lessee', + 'Resource Class', + 'Provision State', + 'Maintenance', + 'Properties', + 'Offer UUID', + 'Lease UUID', + 'Future Offers', + 'Future Leases' + ] + + self.assertEqual(long_collist, list(columns)) + + datalist = ((fakes.node_uuid, + fakes.node_name, + fakes.node_owner, + '', + fakes.lease_resource_class, + '', '', + fakes.formatted_node_properties, + '', '', '', '' + ),) + self.assertEqual(datalist, tuple(data)) diff --git a/esileapclient/tests/unit/osc/v2/test_offer.py b/esileapclient/tests/unit/osc/v2/test_offer.py new file mode 100644 index 0000000..75ecadb --- /dev/null +++ b/esileapclient/tests/unit/osc/v2/test_offer.py @@ -0,0 +1,326 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import json +from osc_lib.tests import utils as osctestutils + +from esileapclient.osc.v2 import offer +from esileapclient.tests.unit.osc import base +from esileapclient.tests.unit.osc import fakes + + +class TestOffer(base.TestESILeapCommand): + + def setUp(self): + super(TestOffer, self).setUp() + + self.client_mock = self.app.client_manager.lease + self.client_mock.reset_mock() + + +class TestOfferCreate(TestOffer): + + def setUp(self): + super(TestOfferCreate, self).setUp() + + self.client_mock.create_offer.return_value = ( + base.FakeResource(copy.deepcopy(fakes.OFFER)) + ) + + # Get the command object to test + self.cmd = offer.CreateOffer(self.app, None) + + def test_offer_create(self): + + arglist = [ + fakes.lease_resource_uuid, + '--end-time', fakes.lease_end_time, + '--lessee', fakes.offer_lessee_id, + '--name', fakes.offer_name, + '--properties', fakes.lease_properties, + '--resource-type', fakes.lease_resource_type, + '--start-time', fakes.lease_start_time, + ] + + verifylist = [ + ('end_time', fakes.lease_end_time), + ('lessee_id', fakes.offer_lessee_id), + ('name', fakes.offer_name), + ('properties', fakes.lease_properties), + ('resource_type', fakes.lease_resource_type), + ('resource_uuid', fakes.lease_resource_uuid), + ('start_time', fakes.lease_start_time), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + args = { + 'end_time': fakes.lease_end_time, + 'lessee_id': fakes.offer_lessee_id, + 'name': fakes.offer_name, + 'properties': json.loads(fakes.lease_properties), + 'resource_type': fakes.lease_resource_type, + 'resource_uuid': fakes.lease_resource_uuid, + 'start_time': fakes.lease_start_time, + } + + self.client_mock.create_offer.assert_called_once_with(**args) + + +class TestOfferList(TestOffer): + def setUp(self): + super(TestOfferList, self).setUp() + + self.client_mock.offers.return_value = [ + base.FakeResource(copy.deepcopy(fakes.OFFER)) + ] + self.cmd = offer.ListOffer(self.app, None) + + def test_offer_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + filters = { + 'status': parsed_args.status, + 'start_time': str(parsed_args.time_range[0]) if + parsed_args.time_range else None, + 'end_time': str(parsed_args.time_range[1]) if + parsed_args.time_range else None, + 'available_start_time': str(parsed_args.availability_range[0]) if + parsed_args.availability_range else None, + 'available_end_time': str(parsed_args.availability_range[1]) if + parsed_args.availability_range else None, + 'project_id': parsed_args.project_id, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + 'resource_class': parsed_args.resource_class + } + + self.client_mock.offers.assert_called_with(**filters) + + collist = [ + "UUID", + "Resource", + "Resource Class", + "Lessee", + "Start Time", + "End Time", + "Status", + "Availabilities", + ] + + self.assertEqual(collist, list(columns)) + + datalist = ((fakes.offer_uuid, + fakes.lease_resource, + fakes.lease_resource_class, + fakes.offer_lessee, + fakes.lease_start_time, + fakes.lease_end_time, + fakes.lease_status, + json.loads(fakes.lease_availabilities) + ),) + self.assertEqual(datalist, tuple(data)) + + def test_offer_list_long(self): + arglist = ['--long'] + verifylist = [('long', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + filters = { + 'status': parsed_args.status, + 'start_time': str(parsed_args.time_range[0]) if + parsed_args.time_range else None, + 'end_time': str(parsed_args.time_range[1]) if + parsed_args.time_range else None, + 'available_start_time': str(parsed_args.availability_range[0]) if + parsed_args.availability_range else None, + 'available_end_time': str(parsed_args.availability_range[1]) if + parsed_args.availability_range else None, + 'project_id': parsed_args.project_id, + 'resource_type': parsed_args.resource_type, + 'resource_uuid': parsed_args.resource_uuid, + 'resource_class': parsed_args.resource_class + } + + self.client_mock.offers.assert_called_with(**filters) + + long_collist = [ + 'UUID', + 'Resource', + 'Resource Class', + 'Resource Properties', + 'Lessee', + 'Start Time', + 'End Time', + 'Status', + 'Availabilities', + 'Project', + 'Parent Lease UUID' + ] + + self.assertEqual(long_collist, list(columns)) + + datalist = ((fakes.offer_uuid, + fakes.lease_resource, + fakes.lease_resource_class, + fakes.formatted_node_properties, + fakes.offer_lessee, + fakes.lease_start_time, + fakes.lease_end_time, + fakes.lease_status, + json.loads(fakes.lease_availabilities), + fakes.lease_project, + fakes.parent_lease_uuid + ),) + self.assertEqual(datalist, tuple(data)) + + +class TestOfferShow(TestOffer): + def setUp(self): + super(TestOfferShow, self).setUp() + + self.client_mock.get_offer.return_value = \ + base.FakeResource(copy.deepcopy(fakes.OFFER)) + + self.cmd = offer.ShowOffer(self.app, None) + + def test_offer_show(self): + arglist = [fakes.offer_uuid] + verifylist = [('uuid', fakes.offer_uuid)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.client_mock.get_offer.assert_called_once_with( + fakes.offer_uuid) + + collist = ( + "availabilities", + "end_time", + "lessee", + "lessee_id", + "name", + "parent_lease_uuid", + "project", + "project_id", + "properties", + "resource", + "resource_class", + "resource_properties", + "resource_type", + "resource_uuid", + "start_time", + "status", + "uuid" + ) + + self.assertEqual(collist, columns) + + datalist = (json.loads(fakes.lease_availabilities), + fakes.lease_end_time, + fakes.offer_lessee, + fakes.offer_lessee_id, + fakes.offer_name, + fakes.parent_lease_uuid, + fakes.lease_project, + fakes.lease_project_id, + json.loads(fakes.lease_properties), + fakes.lease_resource, + fakes.lease_resource_class, + fakes.formatted_node_properties, + fakes.lease_resource_type, + fakes.lease_resource_uuid, + fakes.lease_start_time, + fakes.lease_status, + fakes.offer_uuid, + ) + self.assertEqual(datalist, tuple(data)) + + def test_offer_show_no_id(self): + arglist = [] + verifylist = [] + self.assertRaises(osctestutils.ParserException, + self.check_parser, + self.cmd, arglist, verifylist) + + +class TestOfferDelete(TestOffer): + def setUp(self): + super(TestOfferDelete, self).setUp() + + self.cmd = offer.DeleteOffer(self.app, None) + + def test_offer_delete(self): + arglist = [fakes.offer_uuid] + verifylist = [('uuid', fakes.offer_uuid)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.client_mock.delete_offer.assert_called_once_with( + fakes.offer_uuid) + + def test_offer_delete_no_id(self): + arglist = [] + verifylist = [] + self.assertRaises(osctestutils.ParserException, + self.check_parser, + self.cmd, arglist, verifylist) + + +class TestOfferClaim(TestOffer): + def setUp(self): + super(TestOfferClaim, self).setUp() + + self.cmd = offer.ClaimOffer(self.app, None) + + def test_offer_claim(self): + arglist = [ + fakes.offer_uuid, + '--end-time', fakes.lease_end_time, + '--properties', fakes.lease_properties, + '--start-time', fakes.lease_start_time, + ] + verifylist = [ + ('offer_uuid', fakes.offer_uuid), + ('end_time', fakes.lease_end_time), + ('properties', fakes.lease_properties), + ('start_time', fakes.lease_start_time), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + lease_args = { + 'end_time': fakes.lease_end_time, + 'properties': json.loads(fakes.lease_properties), + 'start_time': fakes.lease_start_time, + } + + self.client_mock.claim_offer.assert_called_once_with( + fakes.offer_uuid, **lease_args) + + def test_offer_claim_no_id(self): + arglist = [] + verifylist = [] + self.assertRaises(osctestutils.ParserException, + self.check_parser, + self.cmd, arglist, verifylist) diff --git a/requirements.txt b/requirements.txt index ab68f99..93cc501 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ openstacksdk<1.3.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0 python-openstackclient>=3.18.0 six>=1.12.0 +esisdk diff --git a/setup.cfg b/setup.cfg index d43ad27..754bdf8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,3 +45,17 @@ openstack.lease.v1 = esi_offer_show = esileapclient.osc.v1.offer:ShowOffer esi_offer_delete = esileapclient.osc.v1.offer:DeleteOffer esi_offer_claim = esileapclient.osc.v1.offer:ClaimOffer + +openstack.lease.v2 = + esi_event_list = esileapclient.osc.v2.event:ListEvent + esi_lease_list = esileapclient.osc.v2.lease:ListLease + esi_lease_create = esileapclient.osc.v2.lease:CreateLease + esi_lease_update = esileapclient.osc.v2.lease:UpdateLease + esi_lease_show = esileapclient.osc.v2.lease:ShowLease + esi_lease_delete = esileapclient.osc.v2.lease:DeleteLease + esi_node_list = esileapclient.osc.v2.node:ListNode + esi_offer_list = esileapclient.osc.v2.offer:ListOffer + esi_offer_create = esileapclient.osc.v2.offer:CreateOffer + esi_offer_show = esileapclient.osc.v2.offer:ShowOffer + esi_offer_delete = esileapclient.osc.v2.offer:DeleteOffer + esi_offer_claim = esileapclient.osc.v2.offer:ClaimOffer diff --git a/test-requirements.txt b/test-requirements.txt index dffd33c..aafb77f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,10 +6,11 @@ mock>=2.0.0 osc-lib>=2.0.0 oslo.serialization>=3.1.0 pycodestyle>=2.5.0 -pytest>= 4.6.3 +pytest<=8.1.2 pytest-cov>=2.7.1 requests-mock>=1.2.0 tempest>=17.1.0 testtools>=2.2.0 tox>= 3.12.1 WebTest>=2.0.33 +esisdk