From 5e4032150d360a305397e0220e51c5a66f2f5313 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 2 May 2012 17:02:08 -0400 Subject: Fix "help" command and implement "list server" and "show server" blueprint client-manager blueprint nova-client bug 992841 Move the authentication logic into a new ClientManager class so that only commands that need to authenticate will trigger that code. Implement "list server" and "show server" commands as examples of using the ClientManager, Lister, and ShowOne classes. Change-Id: I9845b70b33bae4b193dbe41871bf0ca8e286a727 --- openstackclient/compute/v2/server.py | 189 ++++++++++++++++++++++++++--------- 1 file changed, 143 insertions(+), 46 deletions(-) (limited to 'openstackclient/compute/v2') diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index c7c6add0..69bfc7e8 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -20,45 +20,55 @@ Server action implementations """ import logging +import os + +from cliff import lister +from cliff import show from openstackclient.common import command from openstackclient.common import utils -def _find_server(cs, server): - """Get a server by name or ID.""" - return utils.find_resource(cs.servers, server) - - -def _print_server(cs, server): - # By default when searching via name we will do a - # findall(name=blah) and due a REST /details which is not the same - # as a .get() and doesn't get the information about flavors and - # images. This fix it as we redo the call with the id which does a - # .get() to get all informations. - if not 'flavor' in server._info: - server = _find_server(cs, server.id) - - networks = server.networks - info = server._info.copy() - for network_label, address_list in networks.items(): - info['%s network' % network_label] = ', '.join(address_list) - - flavor = info.get('flavor', {}) - flavor_id = flavor.get('id', '') - info['flavor'] = _find_flavor(cs, flavor_id).name - - image = info.get('image', {}) - image_id = image.get('id', '') - info['image'] = _find_image(cs, image_id).name - - info.pop('links', None) - info.pop('addresses', None) - - utils.print_dict(info) - - -class List_Server(command.OpenStackCommand): +def _format_servers_list_networks(server): + """Return a string containing the networks a server is attached to. + + :param server: a single Server resource + """ + output = [] + for (network, addresses) in server.networks.items(): + if not addresses: + continue + addresses_csv = ', '.join(addresses) + group = "%s=%s" % (network, addresses_csv) + output.append(group) + return '; '.join(output) + + +def get_server_properties(server, fields, formatters={}): + """Return a tuple containing the server properties. + + :param server: a single Server resource + :param fields: tuple of strings with the desired field names + :param formatters: dictionary mapping field names to callables + to format the values + """ + row = [] + mixed_case_fields = ['serverId'] + + for field in fields: + if field in formatters: + row.append(formatters[field](server)) + else: + if field in mixed_case_fields: + field_name = field.replace(' ', '_') + else: + field_name = field.lower().replace(' ', '_') + data = getattr(server, field_name, '') + row.append(data) + return tuple(row) + + +class List_Server(command.OpenStackCommand, lister.Lister): "List server command." api = 'compute' @@ -67,17 +77,79 @@ class List_Server(command.OpenStackCommand): def get_parser(self, prog_name): parser = super(List_Server, self).get_parser(prog_name) parser.add_argument( - '--long', + '--reservation-id', + help='only return instances that match the reservation', + ) + parser.add_argument( + '--ip', + help='regular expression to match IP address', + ) + parser.add_argument( + '--ip6', + help='regular expression to match IPv6 address', + ) + parser.add_argument( + '--name', + help='regular expression to match name', + ) + parser.add_argument( + '--instance-name', + help='regular expression to match instance name', + ) + parser.add_argument( + '--status', + help='search by server status', + # FIXME(dhellmann): Add choices? + ) + parser.add_argument( + '--flavor', + help='search by flavor ID', + ) + parser.add_argument( + '--image', + help='search by image ID', + ) + parser.add_argument( + '--host', + metavar='HOSTNAME', + help='search by hostname', + ) + parser.add_argument( + '--all-tenants', action='store_true', - default=False, - help='Additional fields are listed in output') + default=bool(int(os.environ.get("ALL_TENANTS", 0))), + help='display information from all tenants (admin only)', + ) return parser - def run(self, parsed_args): - self.log.info('v2.List_Server.run(%s)' % parsed_args) - - -class Show_Server(command.OpenStackCommand): + def get_data(self, parsed_args): + self.log.debug('v2.List_Server.run(%s)' % parsed_args) + nova_client = self.app.client_manager.compute + search_opts = { + 'all_tenants': parsed_args.all_tenants, + 'reservation_id': parsed_args.reservation_id, + 'ip': parsed_args.ip, + 'ip6': parsed_args.ip6, + 'name': parsed_args.name, + 'image': parsed_args.image, + 'flavor': parsed_args.flavor, + 'status': parsed_args.status, + 'host': parsed_args.host, + 'instance_name': parsed_args.instance_name, + } + self.log.debug('search options: %s', search_opts) + # FIXME(dhellmann): Consider adding other columns + columns = ('ID', 'Name', 'Status', 'Networks') + data = nova_client.servers.list(search_opts=search_opts) + return (columns, + (get_server_properties( + s, columns, + formatters={'Networks': _format_servers_list_networks}, + ) for s in data), + ) + + +class Show_Server(command.OpenStackCommand, show.ShowOne): "Show server command." api = 'compute' @@ -91,7 +163,32 @@ class Show_Server(command.OpenStackCommand): help='Name or ID of server to display') return parser - def run(self, parsed_args): - self.log.info('v2.Show_Server.run(%s)' % parsed_args) - #s = _find_server(cs, args.server) - #_print_server(cs, s) + def get_data(self, parsed_args): + self.log.debug('v2.Show_Server.run(%s)' % parsed_args) + nova_client = self.app.client_manager.compute + server = utils.find_resource(nova_client.servers, parsed_args.server) + + info = {} + info.update(server._info) + + # Convert the flavor blob to a name + flavor_info = info.get('flavor', {}) + flavor_id = flavor_info.get('id', '') + flavor = utils.find_resource(nova_client.flavors, flavor_id) + info['flavor'] = flavor.name + + # Convert the image blob to a name + image_info = info.get('image', {}) + image_id = image_info.get('id', '') + image = utils.find_resource(nova_client.images, image_id) + info['image'] = image.name + + # Format addresses in a useful way + info['addresses'] = _format_servers_list_networks(server) + + # Remove a couple of values that are long and not too useful + info.pop('links', None) + + columns = sorted(info.keys()) + values = [info[c] for c in columns] + return (columns, values) -- cgit v1.2.1