diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..124f63f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,48 @@ +name: Unit tests + +env: + # This should match the default python_version build arg + PYTHON_VERSION: 3.8 + TOX_ENV: py38 + +on: + push: + branches: + - "*" + pull_request: + +jobs: + run_linting: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v3 + + run_tests: + strategy: + matrix: + include: + - python_version: "3.10" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python_version }} + - name: install uv for speedup + run: pip install uv + - name: install test tools + run: | + uv pip install \ + --system \ + -r requirements.txt \ + -r test-requirements.txt \ + . + - name: init stestr repo + run: stestr init + - name: run stestr tests + run: stestr run + - name: list failing tests + run: stestr failing --list + if: always() #run even if tests fail \ No newline at end of file diff --git a/blazarclient/command.py b/blazarclient/command.py index 5a1e875..f1c68cf 100644 --- a/blazarclient/command.py +++ b/blazarclient/command.py @@ -92,7 +92,10 @@ def get_parser(self, prog_name): parser = super(BlazarCommand, self).get_parser(prog_name) return parser - def format_output_data(self, data): + def format_output_data(self, data, parsed_args): + # Do not format output data if the formatter is not table + if parsed_args.formatter != 'table': + return for k, v in data.items(): if isinstance(v, str): try: @@ -106,12 +109,7 @@ def format_output_data(self, data): # NOTE(sbauza): This is not something AST can evaluate, # probably a string. pass - if isinstance(v, list): - value = '\n'.join(utils.dumps( - i, indent=self.json_indent) if isinstance(i, dict) - else str(i) for i in v) - data[k] = value - elif isinstance(v, dict): + elif isinstance(v, list) or isinstance(v, dict): value = utils.dumps(v, indent=self.json_indent) data[k] = value elif v is None: @@ -137,7 +135,7 @@ def get_data(self, parsed_args): body = self.args2body(parsed_args) resource_manager = getattr(blazar_client, self.resource) data = resource_manager.create(**body) - self.format_output_data(data) + self.format_output_data(data, parsed_args) if data: print('Created a new %s:' % self.resource, file=self.app.stdout) @@ -229,13 +227,17 @@ class ListCommand(BlazarCommand, lister.Lister): log = None _formatters = {} list_columns = [] + long_columns = [] unknown_parts_flag = True + _filters = [] def args2body(self, parsed_args): params = {} if parsed_args.sort_by: if parsed_args.sort_by in self.list_columns: params['sort_by'] = parsed_args.sort_by + elif self.long_columns and parsed_args.sort_by in self.long_columns: + params['sort_by'] = parsed_args.sort_by else: msg = 'Invalid sort option %s' % parsed_args.sort_by raise exception.BlazarClientException(msg) @@ -243,6 +245,12 @@ def args2body(self, parsed_args): def get_parser(self, prog_name): parser = super(ListCommand, self).get_parser(prog_name) + if self.long_columns: + parser.add_argument( + '--long', + action='store_true', + help='Display detailed information for each item' + ) return parser def retrieve_list(self, parsed_args): @@ -251,6 +259,8 @@ def retrieve_list(self, parsed_args): body = self.args2body(parsed_args) resource_manager = getattr(blazar_client, self.resource) data = resource_manager.list(**body) + for f in self._filters: + data = list(filter(f, data)) return data def setup_columns(self, info, parsed_args): @@ -269,10 +279,19 @@ def setup_columns(self, info, parsed_args): valid_parsed_columns = {col for col in parsed_args.columns if col in columns} else: valid_parsed_columns = set() - if self.list_columns: + + default_columns = self.list_columns + if self.long_columns and parsed_args.long: + default_columns += self.long_columns + if default_columns: columns = { - col for col in self.list_columns if col in columns - } | valid_parsed_columns + col for col in default_columns if col in columns + } | valid_parsed_columns + # sort the columns based on list_columns + sorting_map = {item: i for i, item in enumerate(default_columns)} + # sort key is either index of item in list_columns, or placed at end of list + columns = sorted(columns, key=lambda x: sorting_map.get(x, len(default_columns))) + return ( columns, (utils.get_item_properties(s, columns, formatters=self._formatters) @@ -288,12 +307,43 @@ def get_data(self, parsed_args): class ListAllocationCommand(ListCommand, lister.Lister): """List allocations that belong to a given tenant.""" + def get_parser(self, prog_name): + parser = super(ListAllocationCommand, self).get_parser(prog_name) + parser.add_argument( + '--lease-id', metavar="", + help='Lease to filter allocations by', + ) + parser.add_argument( + '--username', metavar="", + help='Username to filter allocations by', + ) + return parser + def retrieve_list(self, parsed_args): """Retrieve a list of resources from Blazar server.""" blazar_client = self.get_client() body = self.args2body(parsed_args) resource_manager = getattr(blazar_client, self.resource) data = resource_manager.list_allocations(**body) + filters = [] + if parsed_args.lease_id: + filters.append(lambda x: x['lease_id'] == parsed_args.lease_id) + if parsed_args.username: + filters.append(lambda x: x.get("extras", {}).get('user_name') == parsed_args.username) + + if filters: + new_data = [] + for allocation in data: + new_reservations = [] + for reservation in allocation["reservations"]: + if all(f(reservation) for f in filters): + new_reservations.append(reservation) + if new_reservations: + new_data.append({ + "resource_id": allocation["resource_id"], + "reservations": new_reservations, + }) + data = new_data return data @@ -310,6 +360,7 @@ def get_parser(self, prog_name): def get_data(self, parsed_args): self.log.debug('get_data(%s)' % parsed_args) + body = self.args2body(parsed_args) blazar_client = self.get_client() if self.allow_names: @@ -323,7 +374,9 @@ def get_data(self, parsed_args): resource_manager = getattr(blazar_client, self.resource) data = resource_manager.get(res_id) - self.format_output_data(data) + if body.get('detail'): + data.update(resource_manager.additional_details(res_id)) + self.format_output_data(data, parsed_args) return list(zip(*sorted(data.items()))) @@ -346,7 +399,7 @@ def get_data(self, parsed_args): blazar_client = self.get_client() resource_manager = getattr(blazar_client, self.resource) data = resource_manager.get_allocation(parsed_args.id) - self.format_output_data(data) + self.format_output_data(data, parsed_args) return list(zip(*sorted(data.items()))) @@ -407,8 +460,6 @@ def get_data(self, parsed_args): blazar_client = self.get_client() resource_manager = getattr(blazar_client, self.resource) data = resource_manager.get_property(parsed_args.property_name) - if parsed_args.formatter == 'table': - self.format_output_data(data) return list(zip(*sorted(data.items()))) @@ -447,9 +498,16 @@ def get_parser(self, prog_name): default=False, help='Set property to public.' ) + parser.add_argument( + '--unique', + action='store_true', + default=False, + help='Set capability as unique.' + ) return parser def args2body(self, parsed_args): return dict( property_name=parsed_args.property_name, + is_unique=(parsed_args.unique is True), private=(parsed_args.private is True)) diff --git a/blazarclient/tests/test_command.py b/blazarclient/tests/test_command.py index 0b79371..9f75b39 100644 --- a/blazarclient/tests/test_command.py +++ b/blazarclient/tests/test_command.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import argparse from unittest import mock import testtools @@ -84,10 +85,11 @@ def test_format_output_data(self): 'key_none': None} data_after = {'key_string': 'string_value', 'key_dict': '{"key": "value"}', - 'key_list': '1\n2\n3', + 'key_list': '["1", "2", "3"]', 'key_none': ''} - self.command.format_output_data(data_before) + args = argparse.Namespace(formatter="table") + self.command.format_output_data(data_before, args) self.assertEqual(data_after, data_before) diff --git a/blazarclient/tests/v1/shell_commands/test_floatingips.py b/blazarclient/tests/v1/shell_commands/test_floatingips.py index 9302574..5fe315b 100644 --- a/blazarclient/tests/v1/shell_commands/test_floatingips.py +++ b/blazarclient/tests/v1/shell_commands/test_floatingips.py @@ -104,7 +104,7 @@ def test_show_floatingip(self): show_floatingip, floatingip_manager = self.create_show_command( list_value, get_value) - args = argparse.Namespace(id='84c4d37e-1f8b-45ce-897b-16ad7f49b0e9') + args = argparse.Namespace(id='84c4d37e-1f8b-45ce-897b-16ad7f49b0e9', formatter="table") expected = [('id',), ('84c4d37e-1f8b-45ce-897b-16ad7f49b0e9',)] ret = show_floatingip.get_data(args) diff --git a/blazarclient/tests/v1/shell_commands/test_hosts.py b/blazarclient/tests/v1/shell_commands/test_hosts.py index 53c6df4..8552e84 100644 --- a/blazarclient/tests/v1/shell_commands/test_hosts.py +++ b/blazarclient/tests/v1/shell_commands/test_hosts.py @@ -178,7 +178,7 @@ def test_show_host(self): show_host, host_manager = self.create_show_command(list_value, get_value) - args = argparse.Namespace(id='101') + args = argparse.Namespace(id='101', formatter="table") expected = [('hypervisor_hostname', 'id'), ('host-1', '101')] ret = show_host.get_data(args) @@ -197,7 +197,7 @@ def test_show_host_with_name(self): show_host, host_manager = self.create_show_command(list_value, get_value) - args = argparse.Namespace(id='host-1') + args = argparse.Namespace(id='host-1', formatter="table") expected = [('hypervisor_hostname', 'id'), ('host-1', '101')] ret = show_host.get_data(args) @@ -215,7 +215,7 @@ def test_show_host_with_name_startwith_number(self): show_host, host_manager = self.create_show_command(list_value, get_value) - args = argparse.Namespace(id='1-host') + args = argparse.Namespace(id='1-host', formatter="table") expected = [('hypervisor_hostname', 'id'), ('1-host', '101')] ret = show_host.get_data(args) diff --git a/blazarclient/tests/v1/shell_commands/test_leases.py b/blazarclient/tests/v1/shell_commands/test_leases.py index cdf1e8d..aa2a759 100644 --- a/blazarclient/tests/v1/shell_commands/test_leases.py +++ b/blazarclient/tests/v1/shell_commands/test_leases.py @@ -334,12 +334,33 @@ def test_show_lease(self): ] mock.seal(lease_manager) - args = argparse.Namespace(id=FIRST_LEASE) + args = argparse.Namespace(id=FIRST_LEASE, detail=False, formatter="table") expected = [('id',), (FIRST_LEASE,)] self.assertEqual(show_lease.get_data(args), expected) lease_manager.get.assert_called_once_with(FIRST_LEASE) + def test_show_lease_with_allocations(self): + show_lease, lease_manager = self.create_show_command() + lease_manager.get.return_value = {'id': FIRST_LEASE} + lease_manager.additional_details.return_value = { + 'hosts': [], + 'networks': [], + 'devices': [] + } + lease_manager.list.return_value = [ + {'id': FIRST_LEASE, 'name': 'first-lease'}, + {'id': SECOND_LEASE, 'name': 'second-lease'}, + ] + mock.seal(lease_manager) + + args = argparse.Namespace(id=FIRST_LEASE, detail=True, formatter="table") + expected = [('devices', 'hosts', 'id', 'networks',), ('[]', '[]', FIRST_LEASE, '[]',)] + + self.assertEqual(show_lease.get_data(args), expected) + lease_manager.get.assert_called_once_with(FIRST_LEASE) + lease_manager.additional_details.assert_called_once_with(FIRST_LEASE) + def test_show_lease_by_name(self): show_lease, lease_manager = self.create_show_command() lease_manager.list.return_value = [ @@ -349,13 +370,37 @@ def test_show_lease_by_name(self): lease_manager.get.return_value = {'id': SECOND_LEASE} mock.seal(lease_manager) - args = argparse.Namespace(id='second-lease') + args = argparse.Namespace(id='second-lease', detail=False, formatter="table") expected = [('id',), (SECOND_LEASE,)] self.assertEqual(show_lease.get_data(args), expected) lease_manager.list.assert_called_once_with() lease_manager.get.assert_called_once_with(SECOND_LEASE) + def test_show_lease_by_name_with_allocations(self): + show_lease, lease_manager = self.create_show_command() + lease_manager.list.return_value = [ + {'id': FIRST_LEASE, 'name': 'first-lease'}, + {'id': SECOND_LEASE, 'name': 'second-lease'}, + ] + lease_manager.get.return_value = {'id': SECOND_LEASE} + host1 = {'id': '101', 'hypervisor_hostname': 'host-1'} + lease_manager.additional_details.return_value = { + 'hosts': [host1], + 'networks': [], + 'devices': [] + } + mock.seal(lease_manager) + args = argparse.Namespace(id='second-lease', detail=True, formatter="table") + expected = [ + ('devices', 'hosts', 'id', 'networks',), + ('[]', '[\n {\n "id": "101",\n "hypervisor_hostname": "host-1"\n }\n]', '424d21c3-45a2-448a-81ad-32eddc888375', '[]') + ] + self.assertEqual(show_lease.get_data(args), expected) + lease_manager.list.assert_called_once_with() + lease_manager.get.assert_called_once_with(SECOND_LEASE) + lease_manager.additional_details.assert_called_once_with(SECOND_LEASE) + class DeleteLeaseTestCase(tests.TestCase): diff --git a/blazarclient/tests/v1/shell_commands/test_networks.py b/blazarclient/tests/v1/shell_commands/test_networks.py index d931dba..0ca5ea2 100644 --- a/blazarclient/tests/v1/shell_commands/test_networks.py +++ b/blazarclient/tests/v1/shell_commands/test_networks.py @@ -143,7 +143,7 @@ def test_show_network(self): show_network, network_manager = self.create_show_command(list_value, get_value) - args = argparse.Namespace(id='072c58c0-64ac-467b-b040-9138771e146a') + args = argparse.Namespace(id='072c58c0-64ac-467b-b040-9138771e146a', formatter="table") expected = [('id',), ('072c58c0-64ac-467b-b040-9138771e146a',)] ret = show_network.get_data(args) @@ -232,7 +232,9 @@ def test_list_network_sort_by(self): list_network, network_manager = self.create_list_command() list_networks_args = argparse.Namespace( sort_by='segment_id', - columns=[] + columns=[], + formatter="table", + long=False, ) return_networks = list_network.get_data(list_networks_args) segment_id_index = list(return_networks[0]).index('segment_id') diff --git a/blazarclient/v1/allocations.py b/blazarclient/v1/allocations.py index f1c4c1e..8d8a70a 100644 --- a/blazarclient/v1/allocations.py +++ b/blazarclient/v1/allocations.py @@ -30,5 +30,5 @@ def list(self, resource, sort_by=None): resp, body = self.request_manager.get('/%s/allocations' % resource) allocations = body['allocations'] if sort_by: - allocations = sorted(allocations, key=lambda l: l[sort_by]) + allocations = sorted(allocations, key=lambda alloc: alloc[sort_by]) return allocations diff --git a/blazarclient/v1/devices.py b/blazarclient/v1/devices.py index a2f319f..992169c 100644 --- a/blazarclient/v1/devices.py +++ b/blazarclient/v1/devices.py @@ -50,7 +50,7 @@ def list(self, sort_by=None): resp, body = self.request_manager.get('/devices') devices = body['devices'] if sort_by: - devices = sorted(devices, key=lambda l: l[sort_by]) + devices = sorted(devices, key=lambda dev: dev[sort_by]) return devices def get_allocation(self, device_id): @@ -64,7 +64,7 @@ def list_allocations(self, sort_by=None): resp, body = self.request_manager.get('/devices/allocations') allocations = body['allocations'] if sort_by: - allocations = sorted(allocations, key=lambda l: l[sort_by]) + allocations = sorted(allocations, key=lambda alloc: alloc[sort_by]) return allocations def reallocate(self, device_id, values): @@ -90,7 +90,7 @@ def list_properties(self, detail=False, all=False, sort_by=None): if sort_by: resource_properties = sorted(resource_properties, - key=lambda l: l[sort_by]) + key=lambda prop: prop[sort_by]) return resource_properties def get_property(self, property_name): diff --git a/blazarclient/v1/floatingips.py b/blazarclient/v1/floatingips.py index 029f079..2a65329 100644 --- a/blazarclient/v1/floatingips.py +++ b/blazarclient/v1/floatingips.py @@ -43,5 +43,5 @@ def list(self, sort_by=None): resp, body = self.request_manager.get('/floatingips') floatingips = body['floatingips'] if sort_by: - floatingips = sorted(floatingips, key=lambda l: l[sort_by]) + floatingips = sorted(floatingips, key=lambda ip: ip[sort_by]) return floatingips diff --git a/blazarclient/v1/hosts.py b/blazarclient/v1/hosts.py index 54f7c52..0a1c102 100644 --- a/blazarclient/v1/hosts.py +++ b/blazarclient/v1/hosts.py @@ -51,7 +51,7 @@ def list(self, sort_by=None): resp, body = self.request_manager.get('/os-hosts') hosts = body['hosts'] if sort_by: - hosts = sorted(hosts, key=lambda l: l[sort_by]) + hosts = sorted(hosts, key=lambda host: host[sort_by]) return hosts def get_allocation(self, host_id): @@ -65,7 +65,7 @@ def list_allocations(self, sort_by=None): resp, body = self.request_manager.get('/os-hosts/allocations') allocations = body['allocations'] if sort_by: - allocations = sorted(allocations, key=lambda l: l[sort_by]) + allocations = sorted(allocations, key=lambda alloc: alloc[sort_by]) return allocations def reallocate(self, host_id, values): @@ -96,7 +96,7 @@ def list_properties(self, detail=False, all=False, sort_by=None): if sort_by: resource_properties = sorted(resource_properties, - key=lambda l: l[sort_by]) + key=lambda lease: lease[sort_by]) return resource_properties def get_property(self, property_name): @@ -105,10 +105,10 @@ def get_property(self, property_name): if x['property'] == property_name] if not resource_property: raise exception.ResourcePropertyNotFound() - return resource_property[0] + return {} if not resource_property else resource_property[0] - def set_property(self, property_name, private): - data = {'private': private} + def set_property(self, property_name, private, is_unique=False): + data = {'private': private, 'is_unique': is_unique} resp, body = self.request_manager.patch( '/os-hosts/properties/%s' % property_name, body=data) diff --git a/blazarclient/v1/leases.py b/blazarclient/v1/leases.py index 2fb59cf..b0d802f 100644 --- a/blazarclient/v1/leases.py +++ b/blazarclient/v1/leases.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from concurrent.futures import ThreadPoolExecutor + from oslo_utils import timeutils from blazarclient import base @@ -32,7 +34,7 @@ def create(self, name, start, end, reservations, events, before_end=None): resp, body = self.request_manager.post('/leases', body=values) return body['lease'] - def get(self, lease_id): + def get(self, lease_id, detail=False): """Describes lease specifications such as name, status and locked condition. """ @@ -91,9 +93,38 @@ def list(self, sort_by=None): resp, body = self.request_manager.get('/leases') leases = body['leases'] if sort_by: - leases = sorted(leases, key=lambda l: l[sort_by]) + leases = sorted(leases, key=lambda lease: lease[sort_by]) return leases + def additional_details(self, lease_id): + allocations = {} + with ThreadPoolExecutor() as executor: + # Submit the calls + h_future = executor.submit(self.hosts_in_lease, lease_id) + n_future = executor.submit(self.networks_in_lease, lease_id) + d_future = executor.submit(self.devices_in_lease, lease_id) + + # Retrieve the results + allocations['hosts'] = h_future.result() + allocations['networks'] = n_future.result() + allocations['devices'] = d_future.result() + return allocations + + def hosts_in_lease(self, lease_id): + """List all hosts in lease""" + resp, body = self.request_manager.get(f'/leases/{lease_id}/hosts') + return body['hosts'] + + def networks_in_lease(self, lease_id): + """List all networks in lease""" + resp, body = self.request_manager.get(f'/leases/{lease_id}/networks') + return body['networks'] + + def devices_in_lease(self, lease_id): + """List all devices in lease""" + resp, body = self.request_manager.get(f'/leases/{lease_id}/devices') + return body['devices'] + def _add_lease_date(self, values, lease, key, delta_date, positive_delta): delta_sec = utils.from_elapsed_time_to_delta( delta_date, diff --git a/blazarclient/v1/networks.py b/blazarclient/v1/networks.py index 1e2334e..0136034 100644 --- a/blazarclient/v1/networks.py +++ b/blazarclient/v1/networks.py @@ -52,7 +52,7 @@ def list(self, sort_by=None): resp, body = self.request_manager.get('/networks') networks = body['networks'] if sort_by: - networks = sorted(networks, key=lambda l: l[sort_by]) + networks = sorted(networks, key=lambda network: network[sort_by]) return networks def get_allocation(self, network_id): @@ -66,7 +66,7 @@ def list_allocations(self, sort_by=None): resp, body = self.request_manager.get('/networks/allocations') allocations = body['allocations'] if sort_by: - allocations = sorted(allocations, key=lambda l: l[sort_by]) + allocations = sorted(allocations, key=lambda alloc: alloc[sort_by]) return allocations def list_properties(self, detail=False, all=False, sort_by=None): @@ -86,7 +86,7 @@ def list_properties(self, detail=False, all=False, sort_by=None): if sort_by: resource_properties = sorted(resource_properties, - key=lambda l: l[sort_by]) + key=lambda prop: prop[sort_by]) return resource_properties def get_property(self, property_name): diff --git a/blazarclient/v1/shell_commands/allocations.py b/blazarclient/v1/shell_commands/allocations.py index 7ee195e..938e2e5 100644 --- a/blazarclient/v1/shell_commands/allocations.py +++ b/blazarclient/v1/shell_commands/allocations.py @@ -83,7 +83,6 @@ def get_data(self, parsed_args): filter(lambda d: d['id'] == parsed_args.reservation_id, data['reservations'])) - self.format_output_data(data) return list(zip(*sorted(data.items()))) def args2body(self, parsed_args): diff --git a/blazarclient/v1/shell_commands/devices.py b/blazarclient/v1/shell_commands/devices.py index f9b5f4e..f5577dd 100644 --- a/blazarclient/v1/shell_commands/devices.py +++ b/blazarclient/v1/shell_commands/devices.py @@ -22,7 +22,8 @@ class ListDevices(command.ListCommand): """Print a list of devices.""" resource = 'device' log = logging.getLogger(__name__ + '.ListDevices') - list_columns = ['id', 'name', 'device_type', 'device_driver'] + list_columns = ['id', 'name', 'device_type'] + long_columns = ['machine_name', 'model', 'device_name', 'device_driver', 'reservable'] def get_parser(self, prog_name): parser = super(ListDevices, self).get_parser(prog_name) diff --git a/blazarclient/v1/shell_commands/hosts.py b/blazarclient/v1/shell_commands/hosts.py index c61bcd4..1cdc82f 100644 --- a/blazarclient/v1/shell_commands/hosts.py +++ b/blazarclient/v1/shell_commands/hosts.py @@ -28,6 +28,7 @@ class ListHosts(command.ListCommand): log = logging.getLogger(__name__ + '.ListHosts') list_columns = ['id', 'hypervisor_hostname', 'vcpus', 'memory_mb', 'local_gb'] + long_columns = ['node_name', 'node_type', 'disabled', 'reservable'] def get_parser(self, prog_name): parser = super(ListHosts, self).get_parser(prog_name) @@ -36,8 +37,35 @@ def get_parser(self, prog_name): help='column name used to sort result', default='hypervisor_hostname' ) + parser.add_argument( + '--node-type', metavar="", + help='Node type to filter leases by', + ) + parser.add_argument( + '--reservable', + help='List only reservable hosts', + action='store_true', + default=None, + ) + parser.add_argument( + '--unreservable', + help='List only unreservable hosts', + action='store_false', + dest='reservable', + ) return parser + def get_data(self, parsed_args): + if parsed_args.node_type: + self._filters.append( + lambda x: x['node_type'] == parsed_args.node_type + ) + if parsed_args.reservable is not None: + self._filters.append( + lambda x: x['reservable'] == parsed_args.reservable + ) + return super(ListHosts, self).get_data(parsed_args) + class ShowHost(command.ShowCommand): """Show host details.""" @@ -232,7 +260,7 @@ class ListHostProperties(command.ListCommand): """List host properties.""" resource = 'host' log = logging.getLogger(__name__ + '.ListHostProperties') - list_columns = ['property', 'private', 'property_values'] + list_columns = ['property', 'private', 'property_values', 'is_unique'] def args2body(self, parsed_args): params = { diff --git a/blazarclient/v1/shell_commands/leases.py b/blazarclient/v1/shell_commands/leases.py index a3dec83..19d034f 100644 --- a/blazarclient/v1/shell_commands/leases.py +++ b/blazarclient/v1/shell_commands/leases.py @@ -87,6 +87,7 @@ class ListLeases(command.ListCommand): resource = 'lease' log = logging.getLogger(__name__ + '.ListLeases') list_columns = ['id', 'name', 'start_date', 'end_date'] + long_columns = ["status", "created_at", "degraded"] def get_parser(self, prog_name): parser = super(ListLeases, self).get_parser(prog_name) @@ -95,8 +96,35 @@ def get_parser(self, prog_name): help='column name used to sort result', default='name' ) + parser.add_argument( + '--project-id', metavar="", + help='ID of the project to filter leases by', + ) + parser.add_argument( + '--status', metavar="", + help='status to filter leases by', + ) + parser.add_argument( + '--user', metavar="", + help='User ID to filter leases by', + ) return parser + def get_data(self, parsed_args): + if parsed_args.project_id: + self._filters.append( + lambda x: x['project_id'] == parsed_args.project_id + ) + if parsed_args.status: + self._filters.append( + lambda x: x['status'].lower() == parsed_args.status.lower() + ) + if parsed_args.user: + self._filters.append( + lambda x: x['user_id'] == parsed_args.user + ) + return super(ListLeases, self).get_data(parsed_args) + class ShowLease(command.ShowCommand): """Show details about the given lease.""" @@ -107,6 +135,12 @@ class ShowLease(command.ShowCommand): def get_parser(self, prog_name): parser = super(ShowLease, self).get_parser(prog_name) + parser.add_argument( + '--detail', + action='store_true', + help='Return all resources reserved in lease.', + default=False + ) if self.allow_names: help_str = 'ID or name of %s to look up' else: @@ -115,6 +149,11 @@ def get_parser(self, prog_name): help=help_str % self.resource) return parser + def args2body(self, parsed_args): + params = {} + params['detail'] = parsed_args.detail + return params + class CreateLeaseBase(command.CreateCommand): """Create a lease.""" diff --git a/blazarclient/v1/shell_commands/networks.py b/blazarclient/v1/shell_commands/networks.py index a72aa87..f16bcf0 100644 --- a/blazarclient/v1/shell_commands/networks.py +++ b/blazarclient/v1/shell_commands/networks.py @@ -24,6 +24,7 @@ class ListNetworks(command.ListCommand): resource = 'network' log = logging.getLogger(__name__ + '.ListNetworks') list_columns = ['id', 'network_type', 'physical_network', 'segment_id'] + long_columns = ['stitch_provider'] def get_parser(self, prog_name): parser = super(ListNetworks, self).get_parser(prog_name)