diff options
Diffstat (limited to 'openstackclient')
19 files changed, 986 insertions, 568 deletions
diff --git a/openstackclient/common/exceptions.py b/openstackclient/common/exceptions.py index ee0f7a11..5f81e6a6 100644 --- a/openstackclient/common/exceptions.py +++ b/openstackclient/common/exceptions.py @@ -108,28 +108,3 @@ _code_map = dict((c.http_status, c) for c in [ OverLimit, HTTPNotImplemented ]) - - -def from_response(response, body): - """Return an instance of a ClientException based on an httplib2 response. - - Usage:: - - resp, body = http.request(...) - if resp.status != 200: - raise exception_from_response(resp, body) - """ - cls = _code_map.get(response.status, ClientException) - if body: - if hasattr(body, 'keys'): - error = body[list(body.keys())[0]] - message = error.get('message') - details = error.get('details') - else: - # If we didn't get back a properly formed error message we - # probably couldn't communicate with Keystone at all. - message = "Unable to communicate with image service: %s." % body - details = None - return cls(code=response.status, message=message, details=details) - else: - return cls(code=response.status) diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py index 6212989f..fac4d2e3 100644 --- a/openstackclient/compute/v2/floatingip.py +++ b/openstackclient/compute/v2/floatingip.py @@ -15,8 +15,6 @@ """Floating IP action implementations""" -import six - from openstackclient.common import command from openstackclient.common import utils @@ -47,27 +45,6 @@ class AddFloatingIP(command.Command): server.add_floating_ip(parsed_args.ip_address) -class CreateFloatingIP(command.ShowOne): - """Create new floating IP address""" - - def get_parser(self, prog_name): - parser = super(CreateFloatingIP, self).get_parser(prog_name) - parser.add_argument( - 'pool', - metavar='<pool>', - help='Pool to fetch IP address from (name or ID)', - ) - return parser - - def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - floating_ip = compute_client.floating_ips.create(parsed_args.pool) - - info = {} - info.update(floating_ip._info) - return zip(*sorted(six.iteritems(info))) - - class RemoveFloatingIP(command.Command): """Remove floating IP address from server""" diff --git a/openstackclient/compute/v2/security_group.py b/openstackclient/compute/v2/security_group.py index 734bd78e..ca3bf5dc 100644 --- a/openstackclient/compute/v2/security_group.py +++ b/openstackclient/compute/v2/security_group.py @@ -16,15 +16,12 @@ """Compute v2 Security Group action implementations""" -import six - try: from novaclient.v2 import security_group_rules except ImportError: from novaclient.v1_1 import security_group_rules from openstackclient.common import command -from openstackclient.common import parseractions from openstackclient.common import utils @@ -56,68 +53,6 @@ def _xform_security_group_rule(sgroup): return info -class CreateSecurityGroupRule(command.ShowOne): - """Create a new security group rule""" - - def get_parser(self, prog_name): - parser = super(CreateSecurityGroupRule, self).get_parser(prog_name) - parser.add_argument( - 'group', - metavar='<group>', - help='Create rule in this security group (name or ID)', - ) - parser.add_argument( - "--proto", - metavar="<proto>", - default="tcp", - help="IP protocol (icmp, tcp, udp; default: tcp)", - ) - source_group = parser.add_mutually_exclusive_group() - source_group.add_argument( - "--src-ip", - metavar="<ip-address>", - default="0.0.0.0/0", - help="Source IP address block (may use CIDR notation; default: " - "0.0.0.0/0)", - ) - source_group.add_argument( - "--src-group", - metavar="<group>", - help="Source security group (ID only)", - ) - parser.add_argument( - "--dst-port", - metavar="<port-range>", - default=(0, 0), - action=parseractions.RangeAction, - help="Destination port, may be a range: 137:139 (default: 0; " - "only required for proto tcp and udp)", - ) - return parser - - def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - group = utils.find_resource( - compute_client.security_groups, - parsed_args.group, - ) - if parsed_args.proto.lower() == 'icmp': - from_port, to_port = -1, -1 - else: - from_port, to_port = parsed_args.dst_port - data = compute_client.security_group_rules.create( - group.id, - parsed_args.proto, - from_port, - to_port, - parsed_args.src_ip, - parsed_args.src_group, - ) - - info = _xform_security_group_rule(data._info) - return zip(*sorted(six.iteritems(info))) - - class ListSecurityGroupRule(command.Lister): """List security group rules""" diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 89f5cad9..68037c94 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -44,11 +44,11 @@ class ListService(command.Lister): parser.add_argument( "--host", metavar="<host>", - help="Name of host") + help="List services on specified host (name only)") parser.add_argument( "--service", metavar="<service>", - help="Name of service") + help="List only specified service (name only)") parser.add_argument( "--long", action="store_true", diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 16f2b574..b21d6e96 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -25,6 +25,89 @@ def _get_columns(item): return tuple(sorted(columns)) +def _get_attrs(client_manager, parsed_args): + attrs = {} + network_client = client_manager.network + + if parsed_args.network is not None: + network = network_client.find_network(parsed_args.network, + ignore_missing=False) + attrs['floating_network_id'] = network.id + + if parsed_args.subnet is not None: + subnet = network_client.find_subnet(parsed_args.subnet, + ignore_missing=False) + attrs['subnet_id'] = subnet.id + + if parsed_args.port is not None: + port = network_client.find_port(parsed_args.port, + ignore_missing=False) + attrs['port_id'] = port.id + + if parsed_args.floating_ip_address is not None: + attrs['floating_ip_address'] = parsed_args.floating_ip_address + + if parsed_args.fixed_ip_address is not None: + attrs['fixed_ip_address'] = parsed_args.fixed_ip_address + + return attrs + + +class CreateFloatingIP(common.NetworkAndComputeShowOne): + """Create floating IP""" + + def update_parser_common(self, parser): + # In Compute v2 network, floating IPs could be allocated from floating + # IP pools, which are actually external networks. So deprecate the + # parameter "pool", and use "network" instead. + parser.add_argument( + 'network', + metavar='<network>', + help='Network to allocate floating IP from (name or ID)', + ) + return parser + + def update_parser_network(self, parser): + parser.add_argument( + '--subnet', + metavar='<subnet>', + help="Subnet on which you want to create the floating IP " + "(name or ID)" + ) + parser.add_argument( + '--port', + metavar='<port>', + help="Port to be associated with the floating IP " + "(name or ID)" + ) + parser.add_argument( + '--floating-ip-address', + metavar='<floating-ip-address>', + dest='floating_ip_address', + help="Floating IP address" + ) + parser.add_argument( + '--fixed-ip-address', + metavar='<fixed-ip-address>', + dest='fixed_ip_address', + help="Fixed IP address mapped to the floating IP" + ) + return parser + + def take_action_network(self, client, parsed_args): + attrs = _get_attrs(self.app.client_manager, parsed_args) + obj = client.create_ip(**attrs) + columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + return (columns, data) + + def take_action_compute(self, client, parsed_args): + obj = client.floating_ips.create(parsed_args.network) + columns = _get_columns(obj._info) + data = utils.get_dict_properties(obj._info, columns) + return (columns, data) + + class DeleteFloatingIP(common.NetworkAndComputeCommand): """Delete floating IP""" diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 9309b326..f60995ab 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -16,6 +16,7 @@ import six from openstackclient.common import exceptions +from openstackclient.common import parseractions from openstackclient.common import utils from openstackclient.network import common from openstackclient.network import utils as network_utils @@ -34,6 +35,113 @@ def _get_columns(item): return tuple(sorted(columns)) +def _convert_to_lowercase(string): + return string.lower() + + +class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): + """Create a new security group rule""" + + def update_parser_common(self, parser): + parser.add_argument( + 'group', + metavar='<group>', + help='Create rule in this security group (name or ID)', + ) + # TODO(rtheis): Add support for additional protocols for network. + # Until then, continue enforcing the compute choices. + parser.add_argument( + "--proto", + metavar="<proto>", + default="tcp", + choices=['icmp', 'tcp', 'udp'], + type=_convert_to_lowercase, + help="IP protocol (icmp, tcp, udp; default: tcp)", + ) + source_group = parser.add_mutually_exclusive_group() + source_group.add_argument( + "--src-ip", + metavar="<ip-address>", + default="0.0.0.0/0", + help="Source IP address block (may use CIDR notation; default: " + "0.0.0.0/0)", + ) + source_group.add_argument( + "--src-group", + metavar="<group>", + help="Source security group (name or ID)", + ) + parser.add_argument( + "--dst-port", + metavar="<port-range>", + default=(0, 0), + action=parseractions.RangeAction, + help="Destination port, may be a single port or port range: " + "137:139 (only required for IP protocols tcp and udp)", + ) + return parser + + def take_action_network(self, client, parsed_args): + # Get the security group ID to hold the rule. + security_group_id = client.find_security_group( + parsed_args.group, + ignore_missing=False + ).id + + # Build the create attributes. + attrs = {} + # TODO(rtheis): Add --direction option. Until then, continue + # with the default of 'ingress'. + attrs['direction'] = 'ingress' + # TODO(rtheis): Add --ethertype option. Until then, continue + # with the default of 'IPv4' + attrs['ethertype'] = 'IPv4' + # TODO(rtheis): Add port range support (type and code) for icmp + # protocol. Until then, continue ignoring the port range. + if parsed_args.proto != 'icmp': + attrs['port_range_min'] = parsed_args.dst_port[0] + attrs['port_range_max'] = parsed_args.dst_port[1] + attrs['protocol'] = parsed_args.proto + if parsed_args.src_group is not None: + attrs['remote_group_id'] = client.find_security_group( + parsed_args.src_group, + ignore_missing=False + ).id + else: + attrs['remote_ip_prefix'] = parsed_args.src_ip + attrs['security_group_id'] = security_group_id + + # Create and show the security group rule. + obj = client.create_security_group_rule(**attrs) + columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + return (columns, data) + + def take_action_compute(self, client, parsed_args): + group = utils.find_resource( + client.security_groups, + parsed_args.group, + ) + if parsed_args.proto == 'icmp': + from_port, to_port = -1, -1 + else: + from_port, to_port = parsed_args.dst_port + if parsed_args.src_group is not None: + parsed_args.src_group = utils.find_resource( + client.security_groups, + parsed_args.src_group, + ).id + obj = client.security_group_rules.create( + group.id, + parsed_args.proto, + from_port, + to_port, + parsed_args.src_ip, + parsed_args.src_group, + ) + return _format_security_group_rule_show(obj._info) + + class DeleteSecurityGroupRule(common.NetworkAndComputeCommand): """Delete a security group rule""" diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 794c787f..da4f6536 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -17,6 +17,7 @@ import copy from json.encoder import JSONEncoder from openstackclient.common import command +from openstackclient.common import exceptions from openstackclient.common import parseractions from openstackclient.common import utils from openstackclient.identity import common as identity_common @@ -42,6 +43,39 @@ _formatters = { } +def _get_common_parse_arguments(parser): + parser.add_argument( + '--allocation-pool', + metavar='start=<ip-address>,end=<ip-address>', + dest='allocation_pools', + action=parseractions.MultiKeyValueAction, + required_keys=['start', 'end'], + help='Allocation pool IP addresses for this subnet ' + 'e.g.: start=192.168.199.2,end=192.168.199.254 ' + '(This option can be repeated)', + ) + parser.add_argument( + '--dns-nameserver', + metavar='<dns-nameserver>', + action='append', + dest='dns_nameservers', + help='DNS name server for this subnet ' + '(This option can be repeated)', + ) + parser.add_argument( + '--host-route', + metavar='destination=<subnet>,gateway=<ip-address>', + dest='host_routes', + action=parseractions.MultiKeyValueAction, + required_keys=['destination', 'gateway'], + help='Additional route for this subnet ' + 'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 ' + 'destination: destination subnet (in CIDR notation) ' + 'gateway: nexthop IP address ' + '(This option can be repeated)', + ) + + def _get_columns(item): columns = list(item.keys()) if 'tenant_id' in columns: @@ -70,57 +104,66 @@ def convert_entries_to_gateway(entries): return changed_entries -def _get_attrs(client_manager, parsed_args): +def _get_attrs(client_manager, parsed_args, is_create=True): attrs = {} - if parsed_args.name is not None: + if 'name' in parsed_args and parsed_args.name is not None: attrs['name'] = str(parsed_args.name) - if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity - project_id = identity_common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - - client = client_manager.network - attrs['network_id'] = client.find_network(parsed_args.network, - ignore_missing=False).id - - if parsed_args.subnet_pool is not None: - subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool, - ignore_missing=False) - attrs['subnetpool_id'] = subnet_pool.id - - if parsed_args.use_default_subnet_pool: - attrs['use_default_subnetpool'] = True - if parsed_args.gateway.lower() != 'auto': - if parsed_args.gateway.lower() == 'none': - attrs['gateway_ip'] = None - else: - attrs['gateway_ip'] = parsed_args.gateway - if parsed_args.prefix_length is not None: - attrs['prefixlen'] = parsed_args.prefix_length - if parsed_args.subnet_range is not None: - attrs['cidr'] = parsed_args.subnet_range - if parsed_args.ip_version is not None: - attrs['ip_version'] = parsed_args.ip_version - if parsed_args.ipv6_ra_mode is not None: - attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode - if parsed_args.ipv6_address_mode is not None: - attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode - if parsed_args.allocation_pools is not None: + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + client = client_manager.network + attrs['network_id'] = client.find_network(parsed_args.network, + ignore_missing=False).id + if parsed_args.subnet_pool is not None: + subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool, + ignore_missing=False) + attrs['subnetpool_id'] = subnet_pool.id + if parsed_args.use_default_subnet_pool: + attrs['use_default_subnetpool'] = True + if parsed_args.prefix_length is not None: + attrs['prefixlen'] = parsed_args.prefix_length + if parsed_args.subnet_range is not None: + attrs['cidr'] = parsed_args.subnet_range + if parsed_args.ip_version is not None: + attrs['ip_version'] = parsed_args.ip_version + if parsed_args.ipv6_ra_mode is not None: + attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode + if parsed_args.ipv6_address_mode is not None: + attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode + + if 'gateway' in parsed_args and parsed_args.gateway is not None: + gateway = parsed_args.gateway.lower() + + if not is_create and gateway == 'auto': + raise exceptions.CommandError("Auto option is not available" + " for Subnet Set. Valid options are" + " <ip-address> or none") + elif gateway != 'auto': + if gateway == 'none': + attrs['gateway_ip'] = None + else: + attrs['gateway_ip'] = gateway + if ('allocation_pools' in parsed_args and + parsed_args.allocation_pools is not None): attrs['allocation_pools'] = parsed_args.allocation_pools - if parsed_args.enable_dhcp is not None: - attrs['enable_dhcp'] = parsed_args.enable_dhcp - if parsed_args.dns_nameservers is not None: + if parsed_args.dhcp: + attrs['enable_dhcp'] = True + elif parsed_args.no_dhcp: + attrs['enable_dhcp'] = False + if ('dns_nameservers' in parsed_args and + parsed_args.dns_nameservers is not None): attrs['dns_nameservers'] = parsed_args.dns_nameservers - if parsed_args.host_routes is not None: + if 'host_routes' in parsed_args and parsed_args.host_routes is not None: # Change 'gateway' entry to 'nexthop' to match the API attrs['host_routes'] = convert_entries_to_nexthop( parsed_args.host_routes) - return attrs @@ -163,39 +206,19 @@ class CreateSubnet(command.ShowOne): '(required if --subnet-pool is not specified, ' 'optional otherwise)', ) - parser.add_argument( - '--allocation-pool', - metavar='start=<ip-address>,end=<ip-address>', - dest='allocation_pools', - action=parseractions.MultiKeyValueAction, - required_keys=['start', 'end'], - help='Allocation pool IP addresses for this subnet ' - 'e.g.: start=192.168.199.2,end=192.168.199.254 ' - '(This option can be repeated)', - ) dhcp_enable_group = parser.add_mutually_exclusive_group() dhcp_enable_group.add_argument( '--dhcp', - dest='enable_dhcp', action='store_true', default=True, help='Enable DHCP (default)', ) dhcp_enable_group.add_argument( '--no-dhcp', - dest='enable_dhcp', - action='store_false', + action='store_true', help='Disable DHCP', ) parser.add_argument( - '--dns-nameserver', - metavar='<dns-nameserver>', - action='append', - dest='dns_nameservers', - help='DNS name server for this subnet ' - '(This option can be repeated)', - ) - parser.add_argument( '--gateway', metavar='<gateway>', default='auto', @@ -208,18 +231,6 @@ class CreateSubnet(command.ShowOne): "(default is 'auto')", ) parser.add_argument( - '--host-route', - metavar='destination=<subnet>,gateway=<ip-address>', - dest='host_routes', - action=parseractions.MultiKeyValueAction, - required_keys=['destination', 'gateway'], - help='Additional route for this subnet ' - 'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 ' - 'destination: destination subnet (in CIDR notation) ' - 'gateway: nexthop IP address ' - '(This option can be repeated)', - ) - parser.add_argument( '--ip-version', type=int, default=4, @@ -246,7 +257,7 @@ class CreateSubnet(command.ShowOne): metavar='<network>', help='Network this subnet belongs to (name or ID)', ) - + _get_common_parse_arguments(parser) return parser def take_action(self, parsed_args): @@ -309,6 +320,56 @@ class ListSubnet(command.Lister): ) for s in data)) +class SetSubnet(command.Command): + """Set subnet properties""" + + def get_parser(self, prog_name): + parser = super(SetSubnet, self).get_parser(prog_name) + parser.add_argument( + 'subnet', + metavar="<subnet>", + help=("Subnet to modify (name or ID)") + ) + parser.add_argument( + '--name', + metavar='<name>', + help='Updated name of the subnet', + ) + dhcp_enable_group = parser.add_mutually_exclusive_group() + dhcp_enable_group.add_argument( + '--dhcp', + action='store_true', + default=None, + help='Enable DHCP', + ) + dhcp_enable_group.add_argument( + '--no-dhcp', + action='store_true', + help='Disable DHCP', + ) + parser.add_argument( + '--gateway', + metavar='<gateway>', + help="Specify a gateway for the subnet. The options are: " + " <ip-address>: Specific IP address to use as the gateway " + " 'none': This subnet will not use a gateway " + "e.g.: --gateway 192.168.9.1, --gateway none" + ) + _get_common_parse_arguments(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.find_subnet(parsed_args.subnet, ignore_missing=False) + attrs = _get_attrs(self.app.client_manager, parsed_args, + is_create=False) + if not attrs: + msg = "Nothing specified to be set" + raise exceptions.CommandError(msg) + client.update_subnet(obj, **attrs) + return + + class ShowSubnet(command.ShowOne): """Show subnet details""" diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index d0d8d058..834760fb 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -39,11 +39,11 @@ def _get_attrs(parsed_args): if parsed_args.prefixes is not None: attrs['prefixes'] = parsed_args.prefixes if parsed_args.default_prefix_length is not None: - attrs['default_prefix_length'] = parsed_args.default_prefix_length + attrs['default_prefixlen'] = parsed_args.default_prefix_length if parsed_args.min_prefix_length is not None: - attrs['min_prefix_length'] = parsed_args.min_prefix_length + attrs['min_prefixlen'] = parsed_args.min_prefix_length if parsed_args.max_prefix_length is not None: - attrs['max_prefix_length'] = parsed_args.max_prefix_length + attrs['max_prefixlen'] = parsed_args.max_prefix_length return attrs diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py index 2bb0fbfc..860963eb 100644 --- a/openstackclient/tests/compute/v2/fakes.py +++ b/openstackclient/tests/compute/v2/fakes.py @@ -425,13 +425,13 @@ class FakeSecurityGroupRule(object): # Set default attributes. security_group_rule_attrs = { - 'from_port': -1, + 'from_port': 0, 'group': {}, 'id': 'security-group-rule-id-' + uuid.uuid4().hex, - 'ip_protocol': 'icmp', + 'ip_protocol': 'tcp', 'ip_range': {'cidr': '0.0.0.0/0'}, 'parent_group_id': 'security-group-id-' + uuid.uuid4().hex, - 'to_port': -1, + 'to_port': 0, } # Overwrite default attributes. diff --git a/openstackclient/tests/compute/v2/test_security_group_rule.py b/openstackclient/tests/compute/v2/test_security_group_rule.py index 9a8003f3..42bf2c26 100644 --- a/openstackclient/tests/compute/v2/test_security_group_rule.py +++ b/openstackclient/tests/compute/v2/test_security_group_rule.py @@ -17,7 +17,6 @@ from openstackclient.compute.v2 import security_group from openstackclient.tests.compute.v2 import fakes as compute_fakes from openstackclient.tests import fakes from openstackclient.tests.identity.v2_0 import fakes as identity_fakes -from openstackclient.tests import utils security_group_id = '11' @@ -111,271 +110,6 @@ class TestSecurityGroupRule(compute_fakes.TestComputev2): self.sg_rules_mock.reset_mock() -class TestSecurityGroupRuleCreate(TestSecurityGroupRule): - - columns = ( - 'id', - 'ip_protocol', - 'ip_range', - 'parent_group_id', - 'port_range', - 'remote_security_group', - ) - - def setUp(self): - super(TestSecurityGroupRuleCreate, self).setUp() - - self.secgroups_mock.get.return_value = FakeSecurityGroupRuleResource( - None, - copy.deepcopy(SECURITY_GROUP), - loaded=True, - ) - - # Get the command object to test - self.cmd = security_group.CreateSecurityGroupRule(self.app, None) - - def test_security_group_rule_create_no_options(self): - self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource( - None, - copy.deepcopy(SECURITY_GROUP_RULE), - loaded=True, - ) - - arglist = [ - security_group_name, - ] - verifylist = [ - ('group', security_group_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # SecurityGroupManager.create(name, description) - self.sg_rules_mock.create.assert_called_with( - security_group_id, - 'tcp', - 0, - 0, - security_group_rule_cidr, - None, - ) - - self.assertEqual(self.columns, columns) - datalist = ( - security_group_rule_id, - 'tcp', - security_group_rule_cidr, - security_group_id, - '0:0', - '', - ) - self.assertEqual(datalist, data) - - def test_security_group_rule_create_ftp(self): - sg_rule = copy.deepcopy(SECURITY_GROUP_RULE) - sg_rule['from_port'] = 20 - sg_rule['to_port'] = 21 - self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource( - None, - sg_rule, - loaded=True, - ) - - arglist = [ - security_group_name, - '--dst-port', '20:21', - ] - verifylist = [ - ('group', security_group_name), - ('dst_port', (20, 21)), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # SecurityGroupManager.create(name, description) - self.sg_rules_mock.create.assert_called_with( - security_group_id, - 'tcp', - 20, - 21, - security_group_rule_cidr, - None, - ) - - self.assertEqual(self.columns, columns) - datalist = ( - security_group_rule_id, - 'tcp', - security_group_rule_cidr, - security_group_id, - '20:21', - '', - ) - self.assertEqual(datalist, data) - - def test_security_group_rule_create_ssh(self): - sg_rule = copy.deepcopy(SECURITY_GROUP_RULE) - sg_rule['from_port'] = 22 - sg_rule['to_port'] = 22 - sg_rule['ip_range'] = {} - sg_rule['group'] = {'name': security_group_name} - self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource( - None, - sg_rule, - loaded=True, - ) - - arglist = [ - security_group_name, - '--dst-port', '22', - '--src-group', security_group_id, - ] - verifylist = [ - ('group', security_group_name), - ('dst_port', (22, 22)), - ('src_group', security_group_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # SecurityGroupManager.create(name, description) - self.sg_rules_mock.create.assert_called_with( - security_group_id, - 'tcp', - 22, - 22, - security_group_rule_cidr, - security_group_id, - ) - - self.assertEqual(self.columns, columns) - datalist = ( - security_group_rule_id, - 'tcp', - '', - security_group_id, - '22:22', - security_group_name, - ) - self.assertEqual(datalist, data) - - def test_security_group_rule_create_udp(self): - sg_rule = copy.deepcopy(SECURITY_GROUP_RULE) - sg_rule['ip_protocol'] = 'udp' - self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource( - None, - sg_rule, - loaded=True, - ) - - arglist = [ - security_group_name, - '--proto', 'udp', - ] - verifylist = [ - ('group', security_group_name), - ('proto', 'udp'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # SecurityGroupManager.create(name, description) - self.sg_rules_mock.create.assert_called_with( - security_group_id, - 'udp', - 0, - 0, - security_group_rule_cidr, - None, - ) - - self.assertEqual(self.columns, columns) - datalist = ( - security_group_rule_id, - 'udp', - security_group_rule_cidr, - security_group_id, - '0:0', - '', - ) - self.assertEqual(datalist, data) - - def test_security_group_rule_create_icmp(self): - sg_rule_cidr = '10.0.2.0/24' - sg_rule = copy.deepcopy(SECURITY_GROUP_RULE_ICMP) - sg_rule['ip_range'] = {'cidr': sg_rule_cidr} - self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource( - None, - sg_rule, - loaded=True, - ) - - arglist = [ - security_group_name, - '--proto', 'ICMP', - '--src-ip', sg_rule_cidr, - ] - verifylist = [ - ('group', security_group_name), - ('proto', 'ICMP'), - ('src_ip', sg_rule_cidr) - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # SecurityGroupManager.create(name, description) - self.sg_rules_mock.create.assert_called_with( - security_group_id, - 'ICMP', - -1, - -1, - sg_rule_cidr, - None, - ) - - self.assertEqual(self.columns, columns) - datalist = ( - security_group_rule_id, - 'icmp', - sg_rule_cidr, - security_group_id, - '', - '', - ) - self.assertEqual(datalist, data) - - def test_security_group_rule_create_src_invalid(self): - arglist = [ - security_group_name, - '--proto', 'ICMP', - '--src-ip', security_group_rule_cidr, - '--src-group', security_group_id, - ] - - self.assertRaises(utils.ParserException, - self.check_parser, self.cmd, arglist, []) - - class TestSecurityGroupRuleList(TestSecurityGroupRule): def setUp(self): diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py index 71543274..e35fbe16 100644 --- a/openstackclient/tests/network/v2/fakes.py +++ b/openstackclient/tests/network/v2/fakes.py @@ -510,11 +510,11 @@ class FakeSecurityGroupRule(object): 'direction': 'ingress', 'ethertype': 'IPv4', 'id': 'security-group-rule-id-' + uuid.uuid4().hex, - 'port_range_max': None, - 'port_range_min': None, - 'protocol': None, - 'remote_group_id': 'remote-security-group-id-' + uuid.uuid4().hex, - 'remote_ip_prefix': None, + 'port_range_max': 0, + 'port_range_min': 0, + 'protocol': 'tcp', + 'remote_group_id': None, + 'remote_ip_prefix': '0.0.0.0/0', 'security_group_id': 'security-group-id-' + uuid.uuid4().hex, 'tenant_id': 'project-id-' + uuid.uuid4().hex, } diff --git a/openstackclient/tests/network/v2/test_floating_ip.py b/openstackclient/tests/network/v2/test_floating_ip.py index c9da0fa0..3e261fb5 100644 --- a/openstackclient/tests/network/v2/test_floating_ip.py +++ b/openstackclient/tests/network/v2/test_floating_ip.py @@ -16,6 +16,7 @@ import mock from openstackclient.network.v2 import floating_ip from openstackclient.tests.compute.v2 import fakes as compute_fakes from openstackclient.tests.network.v2 import fakes as network_fakes +from openstackclient.tests import utils as tests_utils # Tests for Neutron network @@ -29,6 +30,115 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2): self.network = self.app.client_manager.network +class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): + + # Fake data for option tests. + floating_network = network_fakes.FakeNetwork.create_one_network() + subnet = network_fakes.FakeSubnet.create_one_subnet() + port = network_fakes.FakePort.create_one_port() + + # The floating ip to be deleted. + floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip( + attrs={ + 'floating_network_id': floating_network.id, + 'port_id': port.id, + } + ) + + columns = ( + 'dns_domain', + 'dns_name', + 'fixed_ip_address', + 'floating_ip_address', + 'floating_network_id', + 'id', + 'port_id', + 'project_id', + 'router_id', + 'status', + ) + + data = ( + floating_ip.dns_domain, + floating_ip.dns_name, + floating_ip.fixed_ip_address, + floating_ip.floating_ip_address, + floating_ip.floating_network_id, + floating_ip.id, + floating_ip.port_id, + floating_ip.project_id, + floating_ip.router_id, + floating_ip.status, + ) + + def setUp(self): + super(TestCreateFloatingIPNetwork, self).setUp() + + self.network.create_ip = mock.Mock(return_value=self.floating_ip) + + self.network.find_network = mock.Mock( + return_value=self.floating_network) + self.network.find_subnet = mock.Mock(return_value=self.subnet) + self.network.find_port = mock.Mock(return_value=self.port) + + # Get the command object to test + self.cmd = floating_ip.CreateFloatingIP(self.app, self.namespace) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.floating_ip.floating_network_id, + ] + verifylist = [ + ('network', self.floating_ip.floating_network_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_ip.assert_called_once_with(**{ + 'floating_network_id': self.floating_ip.floating_network_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + '--subnet', self.subnet.id, + '--port', self.floating_ip.port_id, + '--floating-ip-address', self.floating_ip.floating_ip_address, + '--fixed-ip-address', self.floating_ip.fixed_ip_address, + self.floating_ip.floating_network_id, + ] + verifylist = [ + ('subnet', self.subnet.id), + ('port', self.floating_ip.port_id), + ('floating_ip_address', self.floating_ip.floating_ip_address), + ('fixed_ip_address', self.floating_ip.fixed_ip_address), + ('network', self.floating_ip.floating_network_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_ip.assert_called_once_with(**{ + 'subnet_id': self.subnet.id, + 'port_id': self.floating_ip.port_id, + 'floating_ip_address': self.floating_ip.floating_ip_address, + 'fixed_ip_address': self.floating_ip.fixed_ip_address, + 'floating_network_id': self.floating_ip.floating_network_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): # The floating ip to be deleted. @@ -169,6 +279,62 @@ class TestFloatingIPCompute(compute_fakes.TestComputev2): self.compute = self.app.client_manager.compute +class TestCreateFloatingIPCompute(TestFloatingIPCompute): + + # The floating ip to be deleted. + floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip() + + columns = ( + 'fixed_ip', + 'id', + 'instance_id', + 'ip', + 'pool', + ) + + data = ( + floating_ip.fixed_ip, + floating_ip.id, + floating_ip.instance_id, + floating_ip.ip, + floating_ip.pool, + ) + + def setUp(self): + super(TestCreateFloatingIPCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.floating_ips.create.return_value = self.floating_ip + + # Get the command object to test + self.cmd = floating_ip.CreateFloatingIP(self.app, None) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.floating_ip.pool, + ] + verifylist = [ + ('network', self.floating_ip.pool), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.floating_ips.create.assert_called_once_with( + self.floating_ip.pool) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + class TestDeleteFloatingIPCompute(TestFloatingIPCompute): # The floating ip to be deleted. diff --git a/openstackclient/tests/network/v2/test_security_group_rule.py b/openstackclient/tests/network/v2/test_security_group_rule.py index db15d0e2..81b9e18b 100644 --- a/openstackclient/tests/network/v2/test_security_group_rule.py +++ b/openstackclient/tests/network/v2/test_security_group_rule.py @@ -39,6 +39,317 @@ class TestSecurityGroupRuleCompute(compute_fakes.TestComputev2): self.compute = self.app.client_manager.compute +class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): + + # The security group rule to be created. + _security_group_rule = None + + # The security group that will contain the rule created. + _security_group = \ + network_fakes.FakeSecurityGroup.create_one_security_group() + + expected_columns = ( + 'direction', + 'ethertype', + 'id', + 'port_range_max', + 'port_range_min', + 'project_id', + 'protocol', + 'remote_group_id', + 'remote_ip_prefix', + 'security_group_id', + ) + + expected_data = None + + def _setup_security_group_rule(self, attrs=None): + self._security_group_rule = \ + network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( + attrs) + self.network.create_security_group_rule = mock.Mock( + return_value=self._security_group_rule) + self.expected_data = ( + self._security_group_rule.direction, + self._security_group_rule.ethertype, + self._security_group_rule.id, + self._security_group_rule.port_range_max, + self._security_group_rule.port_range_min, + self._security_group_rule.project_id, + self._security_group_rule.protocol, + self._security_group_rule.remote_group_id, + self._security_group_rule.remote_ip_prefix, + self._security_group_rule.security_group_id, + ) + + def setUp(self): + super(TestCreateSecurityGroupRuleNetwork, self).setUp() + + self.network.find_security_group = mock.Mock( + return_value=self._security_group) + + # Get the command object to test + self.cmd = security_group_rule.CreateSecurityGroupRule( + self.app, self.namespace) + + def test_create_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_create_source_group_and_ip(self): + arglist = [ + '--src-ip', '10.10.0.0/24', + '--src-group', self._security_group.id, + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_bad_protocol(self): + arglist = [ + '--protocol', 'foo', + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_default_rule(self): + self._setup_security_group_rule({ + 'port_range_max': 443, + 'port_range_min': 443, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.port_range_min), + self._security_group.id, + ] + verifylist = [ + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_max': self._security_group_rule.port_range_max, + 'port_range_min': self._security_group_rule.port_range_min, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(tuple(self.expected_columns), columns) + self.assertEqual(self.expected_data, data) + + def test_create_source_group(self): + self._setup_security_group_rule({ + 'port_range_max': 22, + 'port_range_min': 22, + 'remote_group_id': self._security_group.id, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.port_range_min), + '--src-group', self._security_group.name, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.port_range_min, + self._security_group_rule.port_range_max)), + ('src_group', self._security_group.name), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'port_range_max': self._security_group_rule.port_range_max, + 'port_range_min': self._security_group_rule.port_range_min, + 'protocol': self._security_group_rule.protocol, + 'remote_group_id': self._security_group_rule.remote_group_id, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(tuple(self.expected_columns), columns) + self.assertEqual(self.expected_data, data) + + def test_create_source_ip(self): + self._setup_security_group_rule({ + 'protocol': 'icmp', + 'port_range_max': -1, + 'port_range_min': -1, + 'remote_ip_prefix': '10.0.2.0/24', + }) + arglist = [ + '--proto', self._security_group_rule.protocol, + '--src-ip', self._security_group_rule.remote_ip_prefix, + self._security_group.id, + ] + verifylist = [ + ('proto', self._security_group_rule.protocol), + ('src_ip', self._security_group_rule.remote_ip_prefix), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(tuple(self.expected_columns), columns) + self.assertEqual(self.expected_data, data) + + +class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): + + # The security group rule to be created. + _security_group_rule = None + + # The security group that will contain the rule created. + _security_group = \ + compute_fakes.FakeSecurityGroup.create_one_security_group() + + def _setup_security_group_rule(self, attrs=None): + self._security_group_rule = \ + compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule( + attrs) + self.compute.security_group_rules.create.return_value = \ + self._security_group_rule + expected_columns, expected_data = \ + security_group_rule._format_security_group_rule_show( + self._security_group_rule._info) + return expected_columns, expected_data + + def setUp(self): + super(TestCreateSecurityGroupRuleCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.security_groups.get.return_value = self._security_group + + # Get the command object to test + self.cmd = security_group_rule.CreateSecurityGroupRule(self.app, None) + + def test_create_no_options(self): + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, [], []) + + def test_create_source_group_and_ip(self): + arglist = [ + '--src-ip', '10.10.0.0/24', + '--src-group', self._security_group.id, + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_bad_protocol(self): + arglist = [ + '--protocol', 'foo', + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + + def test_create_default_rule(self): + expected_columns, expected_data = self._setup_security_group_rule() + dst_port = str(self._security_group_rule.from_port) + ':' + \ + str(self._security_group_rule.to_port) + arglist = [ + '--dst-port', dst_port, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.from_port, + self._security_group_rule.to_port)), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + None, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + def test_create_source_group(self): + expected_columns, expected_data = self._setup_security_group_rule({ + 'from_port': 22, + 'to_port': 22, + 'group': {'name': self._security_group.name}, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.from_port), + '--src-group', self._security_group.name, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.from_port, + self._security_group_rule.to_port)), + ('src_group', self._security_group.name), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + self._security_group.id, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + def test_create_source_ip(self): + expected_columns, expected_data = self._setup_security_group_rule({ + 'ip_protocol': 'icmp', + 'from_port': -1, + 'to_port': -1, + 'ip_range': {'cidr': '10.0.2.0/24'}, + }) + arglist = [ + '--proto', self._security_group_rule.ip_protocol, + '--src-ip', self._security_group_rule.ip_range['cidr'], + self._security_group.id, + ] + verifylist = [ + ('proto', self._security_group_rule.ip_protocol), + ('src_ip', self._security_group_rule.ip_range['cidr']), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + None, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group rule to be deleted. @@ -68,7 +379,7 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): result = self.cmd.take_action(parsed_args) - self.network.delete_security_group_rule.assert_called_with( + self.network.delete_security_group_rule.assert_called_once_with( self._security_group_rule) self.assertIsNone(result) @@ -98,7 +409,7 @@ class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): result = self.cmd.take_action(parsed_args) - self.compute.security_group_rules.delete.assert_called_with( + self.compute.security_group_rules.delete.assert_called_once_with( self._security_group_rule.id) self.assertIsNone(result) @@ -160,7 +471,7 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): columns, data = self.cmd.take_action(parsed_args) - self.network.find_security_group_rule.assert_called_with( + self.network.find_security_group_rule.assert_called_once_with( self._security_group_rule.id, ignore_missing=False) self.assertEqual(tuple(self.columns), columns) self.assertEqual(self.data, data) @@ -207,6 +518,6 @@ class TestShowSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): columns, data = self.cmd.take_action(parsed_args) - self.compute.security_groups.list.assert_called_with() + self.compute.security_groups.list.assert_called_once_with() self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/network/v2/test_subnet.py b/openstackclient/tests/network/v2/test_subnet.py index de17c789..2535bbe6 100644 --- a/openstackclient/tests/network/v2/test_subnet.py +++ b/openstackclient/tests/network/v2/test_subnet.py @@ -14,6 +14,7 @@ import copy import mock +from openstackclient.common import exceptions from openstackclient.common import utils from openstackclient.network.v2 import subnet as subnet_v2 from openstackclient.tests import fakes @@ -203,9 +204,9 @@ class TestCreateSubnet(TestSubnet): self.network.find_network = mock.Mock(return_value=self._network) arglist = [ - self._subnet.name, "--subnet-range", self._subnet.cidr, "--network", self._subnet.network_id, + self._subnet.name, ] verifylist = [ ('name', self._subnet.name), @@ -266,7 +267,7 @@ class TestCreateSubnet(TestSubnet): ('ip_version', self._subnet_from_pool.ip_version), ('gateway', self._subnet_from_pool.gateway_ip), ('dns_nameservers', self._subnet_from_pool.dns_nameservers), - ('enable_dhcp', self._subnet_from_pool.enable_dhcp), + ('dhcp', self._subnet_from_pool.enable_dhcp), ('host_routes', subnet_v2.convert_entries_to_gateway( self._subnet_from_pool.host_routes)), ('subnet_pool', self._subnet_from_pool.subnetpool_id), @@ -332,7 +333,7 @@ class TestCreateSubnet(TestSubnet): ('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode), ('gateway', self._subnet_ipv6.gateway_ip), ('dns_nameservers', self._subnet_ipv6.dns_nameservers), - ('enable_dhcp', self._subnet_ipv6.enable_dhcp), + ('dhcp', self._subnet_ipv6.enable_dhcp), ('host_routes', subnet_v2.convert_entries_to_gateway( self._subnet_ipv6.host_routes)), ('allocation_pools', self._subnet_ipv6.allocation_pools), @@ -469,6 +470,73 @@ class TestListSubnet(TestSubnet): self.assertEqual(self.data_long, list(data)) +class TestSetSubnet(TestSubnet): + + _subnet = network_fakes.FakeSubnet.create_one_subnet() + + def setUp(self): + super(TestSetSubnet, self).setUp() + self.network.update_subnet = mock.Mock(return_value=None) + self.network.find_subnet = mock.Mock(return_value=self._subnet) + self.cmd = subnet_v2.SetSubnet(self.app, self.namespace) + + def test_set_this(self): + arglist = [ + "--name", "new_subnet", + "--dhcp", + "--gateway", self._subnet.gateway_ip, + self._subnet.name, + ] + verifylist = [ + ('name', "new_subnet"), + ('dhcp', True), + ('gateway', self._subnet.gateway_ip), + ('subnet', self._subnet.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'enable_dhcp': True, + 'gateway_ip': self._subnet.gateway_ip, + 'name': "new_subnet", + } + self.network.update_subnet.assert_called_with(self._subnet, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + "--name", "new_subnet", + "--no-dhcp", + "--gateway", "none", + self._subnet.name, + ] + verifylist = [ + ('name', "new_subnet"), + ('no_dhcp', True), + ('gateway', "none"), + ('subnet', self._subnet.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'enable_dhcp': False, + 'gateway_ip': None, + 'name': "new_subnet", + } + self.network.update_subnet.assert_called_with(self._subnet, **attrs) + self.assertIsNone(result) + + def test_set_nothing(self): + arglist = [self._subnet.name, ] + verifylist = [('subnet', self._subnet.name)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + class TestShowSubnet(TestSubnet): # The subnets to be shown _subnet = network_fakes.FakeSubnet.create_one_subnet() diff --git a/openstackclient/tests/network/v2/test_subnet_pool.py b/openstackclient/tests/network/v2/test_subnet_pool.py index ebedaf64..6ed2352d 100644 --- a/openstackclient/tests/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/network/v2/test_subnet_pool.py @@ -119,9 +119,9 @@ class TestCreateSubnetPool(TestSubnetPool): columns, data = (self.cmd.take_action(parsed_args)) self.network.create_subnet_pool.assert_called_once_with(**{ - 'default_prefix_length': self._subnet_pool.default_prefixlen, - 'max_prefix_length': self._subnet_pool.max_prefixlen, - 'min_prefix_length': self._subnet_pool.min_prefixlen, + 'default_prefixlen': self._subnet_pool.default_prefixlen, + 'max_prefixlen': self._subnet_pool.max_prefixlen, + 'min_prefixlen': self._subnet_pool.min_prefixlen, 'name': self._subnet_pool.name, }) self.assertEqual(self.columns, columns) @@ -278,8 +278,8 @@ class TestSetSubnetPool(TestSubnetPool): attrs = { 'name': 'noob', - 'default_prefix_length': '8', - 'min_prefix_length': '8', + 'default_prefixlen': '8', + 'min_prefixlen': '8', } self.network.update_subnet_pool.assert_called_once_with( self._subnet_pool, **attrs) @@ -305,7 +305,7 @@ class TestSetSubnetPool(TestSubnetPool): prefixes.extend(self._subnet_pool.prefixes) attrs = { 'prefixes': prefixes, - 'max_prefix_length': '16', + 'max_prefixlen': '16', } self.network.update_subnet_pool.assert_called_once_with( self._subnet_pool, **attrs) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 90827d20..29c197ef 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -31,20 +31,30 @@ class CreateVolume(command.ShowOne): parser.add_argument( 'name', metavar='<name>', - help='New volume name', + help='Volume name', ) parser.add_argument( '--size', metavar='<size>', required=True, type=int, - help='New volume size in GB', + help='Volume size in GB', + ) + parser.add_argument( + '--type', + metavar='<volume-type>', + help="Set the type of volume", + ) + parser.add_argument( + '--image', + metavar='<image>', + help='Use <image> as source of volume (name or ID)', ) snapshot_group = parser.add_mutually_exclusive_group() snapshot_group.add_argument( '--snapshot', metavar='<snapshot>', - help='Use <snapshot> as source of new volume', + help='Use <snapshot> as source of volume (name or ID)', ) snapshot_group.add_argument( '--snapshot-id', @@ -52,14 +62,14 @@ class CreateVolume(command.ShowOne): help=argparse.SUPPRESS, ) parser.add_argument( - '--description', - metavar='<description>', - help='New volume description', + '--source', + metavar='<volume>', + help='Volume to clone (name or ID)', ) parser.add_argument( - '--type', - metavar='<volume-type>', - help='Use <volume-type> as the new volume type', + '--description', + metavar='<description>', + help='Volume description', ) parser.add_argument( '--user', @@ -74,17 +84,7 @@ class CreateVolume(command.ShowOne): parser.add_argument( '--availability-zone', metavar='<availability-zone>', - help='Create new volume in <availability-zone>', - ) - parser.add_argument( - '--image', - metavar='<image>', - help='Use <image> as source of new volume (name or ID)', - ) - parser.add_argument( - '--source', - metavar='<volume>', - help='Volume to clone (name or ID)', + help='Create volume in <availability-zone>', ) parser.add_argument( '--property', @@ -308,7 +308,7 @@ class SetVolume(command.Command): parser.add_argument( 'volume', metavar='<volume>', - help='Volume to change (name or ID)', + help='Volume to modify (name or ID)', ) parser.add_argument( '--name', @@ -330,7 +330,7 @@ class SetVolume(command.Command): '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Property to add or modify for this volume ' + help='Set a property on this volume ' '(repeat option to set multiple properties)', ) return parser @@ -411,7 +411,7 @@ class UnsetVolume(command.Command): metavar='<key>', action='append', default=[], - help='Property to remove from volume ' + help='Remove a property from volume ' '(repeat option to remove multiple properties)', required=True, ) diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py index 24d0b235..05671c1f 100644 --- a/openstackclient/volume/v1/volume_type.py +++ b/openstackclient/volume/v1/volume_type.py @@ -30,13 +30,13 @@ class CreateVolumeType(command.ShowOne): parser.add_argument( 'name', metavar='<name>', - help='New volume type name', + help='Volume type name', ) parser.add_argument( '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Property to add for this volume type ' + help='Set a property on this volume type ' '(repeat option to set multiple properties)', ) return parser @@ -114,7 +114,7 @@ class SetVolumeType(command.Command): '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Property to add or modify for this volume type ' + help='Set a property on this volume type ' '(repeat option to set multiple properties)', ) return parser @@ -128,6 +128,27 @@ class SetVolumeType(command.Command): volume_type.set_keys(parsed_args.property) +class ShowVolumeType(command.ShowOne): + """Display volume type details""" + + def get_parser(self, prog_name): + parser = super(ShowVolumeType, self).get_parser(prog_name) + parser.add_argument( + "volume_type", + metavar="<volume-type>", + help="Volume type to display (name or ID)" + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + volume_type = utils.find_resource( + volume_client.volume_types, parsed_args.volume_type) + properties = utils.format_dict(volume_type._info.pop('extra_specs')) + volume_type._info.update({'properties': properties}) + return zip(*sorted(six.iteritems(volume_type._info))) + + class UnsetVolumeType(command.Command): """Unset volume type properties""" @@ -143,7 +164,7 @@ class UnsetVolumeType(command.Command): metavar='<key>', action='append', default=[], - help='Property to remove from volume type ' + help='Remove a property from this volume type ' '(repeat option to remove multiple properties)', required=True, ) @@ -160,24 +181,3 @@ class UnsetVolumeType(command.Command): volume_type.unset_keys(parsed_args.property) else: self.app.log.error("No changes requested\n") - - -class ShowVolumeType(command.ShowOne): - """Display volume type details""" - - def get_parser(self, prog_name): - parser = super(ShowVolumeType, self).get_parser(prog_name) - parser.add_argument( - "volume_type", - metavar="<volume-type>", - help="Volume type to display (name or ID)" - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type) - properties = utils.format_dict(volume_type._info.pop('extra_specs')) - volume_type._info.update({'properties': properties}) - return zip(*sorted(six.iteritems(volume_type._info))) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 5d9d2d9e..5b7511e8 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -32,29 +32,39 @@ class CreateVolume(command.ShowOne): parser.add_argument( "name", metavar="<name>", - help="New volume name" + help="Volume name", ) parser.add_argument( "--size", metavar="<size>", type=int, required=True, - help="New volume size in GB" + help="Volume size in GB", + ) + parser.add_argument( + "--type", + metavar="<volume-type>", + help="Set the type of volume", + ) + parser.add_argument( + "--image", + metavar="<image>", + help="Use <image> as source of volume (name or ID)", ) parser.add_argument( "--snapshot", metavar="<snapshot>", - help="Use <snapshot> as source of new volume (name or ID)" + help="Use <snapshot> as source of volume (name or ID)", ) parser.add_argument( - "--description", - metavar="<description>", - help="New volume description" + "--source", + metavar="<volume>", + help="Volume to clone (name or ID)", ) parser.add_argument( - "--type", - metavar="<volume-type>", - help="Use <volume-type> as the new volume type", + "--description", + metavar="<description>", + help="Volume description", ) parser.add_argument( '--user', @@ -69,24 +79,14 @@ class CreateVolume(command.ShowOne): parser.add_argument( "--availability-zone", metavar="<availability-zone>", - help="Create new volume in <availability_zone>" - ) - parser.add_argument( - "--image", - metavar="<image>", - help="Use <image> as source of new volume (name or ID)" - ) - parser.add_argument( - "--source", - metavar="<volume>", - help="Volume to clone (name or ID)" + help="Create volume in <availability-zone>", ) parser.add_argument( "--property", metavar="<key=value>", action=parseractions.KeyValueAction, help="Set a property to this volume " - "(repeat option to set multiple properties)" + "(repeat option to set multiple properties)", ) return parser @@ -188,13 +188,13 @@ class ListVolume(command.Lister): parser = super(ListVolume, self).get_parser(prog_name) parser.add_argument( '--project', - metavar='<project-id>', + metavar='<project>', help='Filter results by project (name or ID) (admin only)' ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--user', - metavar='<user-id>', + metavar='<user>', help='Filter results by user (name or ID) (admin only)' ) identity_common.add_user_domain_option_to_parser(parser) @@ -320,7 +320,7 @@ class SetVolume(command.Command): parser.add_argument( 'volume', metavar='<volume>', - help='Volume to change (name or ID)', + help='Volume to modify (name or ID)', ) parser.add_argument( '--name', @@ -328,28 +328,28 @@ class SetVolume(command.Command): help='New volume name', ) parser.add_argument( - '--description', - metavar='<description>', - help='New volume description', - ) - parser.add_argument( '--size', metavar='<size>', type=int, help='Extend volume size in GB', ) parser.add_argument( + '--description', + metavar='<description>', + help='New volume description', + ) + parser.add_argument( '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Property to add or modify for this volume ' + help='Set a property on this volume ' '(repeat option to set multiple properties)', ) parser.add_argument( '--image-property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='To add or modify image properties for this volume ' + help='Set an image property on this volume ' '(repeat option to set multiple image properties)', ) return parser @@ -434,14 +434,14 @@ class UnsetVolume(command.Command): '--property', metavar='<key>', action='append', - help='Property to remove from volume ' + help='Remove a property from volume ' '(repeat option to remove multiple properties)', ) parser.add_argument( '--image-property', metavar='<key>', action='append', - help='To remove image properties from volume ' + help='Remove an image property from volume ' '(repeat option to remove multiple image properties)', ) return parser diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index d2b3ed6a..5509ac52 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -29,12 +29,12 @@ class CreateVolumeType(command.ShowOne): parser.add_argument( "name", metavar="<name>", - help="New volume type name" + help="Volume type name", ) parser.add_argument( "--description", metavar="<description>", - help="New volume type description", + help="Volume type description", ) public_group = parser.add_mutually_exclusive_group() public_group.add_argument( @@ -55,7 +55,7 @@ class CreateVolumeType(command.ShowOne): '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Property to add for this volume type' + help='Set a property on this volume type' '(repeat option to set multiple properties)', ) return parser @@ -153,7 +153,7 @@ class SetVolumeType(command.Command): '--property', metavar='<key=value>', action=parseractions.KeyValueAction, - help='Property to add or modify for this volume type ' + help='Set a property on this volume type ' '(repeat option to set multiple properties)', ) return parser @@ -221,7 +221,7 @@ class UnsetVolumeType(command.Command): metavar='<key>', default=[], required=True, - help='Property to remove from volume type ' + help='Remove a property from this volume type ' '(repeat option to remove multiple properties)', ) return parser |
