diff options
Diffstat (limited to 'openstackclient/network')
| -rw-r--r-- | openstackclient/network/common.py | 37 | ||||
| -rw-r--r-- | openstackclient/network/v2/address_scope.py | 2 | ||||
| -rw-r--r-- | openstackclient/network/v2/network.py | 61 | ||||
| -rw-r--r-- | openstackclient/network/v2/port.py | 2 | ||||
| -rw-r--r-- | openstackclient/network/v2/router.py | 22 | ||||
| -rw-r--r-- | openstackclient/network/v2/security_group_rule.py | 188 | ||||
| -rw-r--r-- | openstackclient/network/v2/subnet.py | 6 | ||||
| -rw-r--r-- | openstackclient/network/v2/subnet_pool.py | 2 |
8 files changed, 253 insertions, 67 deletions
diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 1e2c4cce..a3047d84 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -15,6 +15,7 @@ import abc import six from openstackclient.common import command +from openstackclient.common import exceptions @six.add_metaclass(abc.ABCMeta) @@ -69,6 +70,42 @@ class NetworkAndComputeCommand(command.Command): @six.add_metaclass(abc.ABCMeta) +class NetworkAndComputeDelete(NetworkAndComputeCommand): + """Network and Compute Delete + + Delete class for commands that support implementation via + the network or compute endpoint. Such commands have different + implementations for take_action() and may even have different + arguments. This class supports bulk deletion, and error handling + following the rules in doc/source/command-errors.rst. + """ + + def take_action(self, parsed_args): + ret = 0 + resources = getattr(parsed_args, self.resource, []) + + for r in resources: + self.r = r + try: + if self.app.client_manager.is_network_endpoint_enabled(): + self.take_action_network(self.app.client_manager.network, + parsed_args) + else: + self.take_action_compute(self.app.client_manager.compute, + parsed_args) + except Exception as e: + self.app.log.error("Failed to delete %s with name or ID " + "'%s': %s" % (self.resource, r, e)) + ret += 1 + + if ret: + total = len(resources) + msg = "%s of %s %ss failed to delete." % (ret, total, + self.resource) + raise exceptions.CommandError(msg) + + +@six.add_metaclass(abc.ABCMeta) class NetworkAndComputeLister(command.Lister): """Network and Compute Lister diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index fac0849f..614900c9 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -185,7 +185,7 @@ class SetAddressScope(command.Command): if parsed_args.no_share: attrs['shared'] = False if attrs == {}: - msg = "Nothing specified to be set." + msg = _("Nothing specified to be set.") raise exceptions.CommandError(msg) client.update_address_scope(obj, **attrs) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 4b77971a..bf01e2ec 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -32,7 +32,7 @@ def _format_router_external(item): _formatters = { 'subnets': utils.format_list, 'admin_state_up': _format_admin_state, - 'router_external': _format_router_external, + 'router:external': _format_router_external, 'availability_zones': utils.format_list, 'availability_zone_hints': utils.format_list, } @@ -43,9 +43,6 @@ def _get_columns(item): if 'tenant_id' in columns: columns.remove('tenant_id') columns.append('project_id') - if 'router:external' in columns: - columns.remove('router:external') - columns.append('router_external') return tuple(sorted(columns)) @@ -93,11 +90,17 @@ def _get_attrs(client_manager, parsed_args): attrs['provider:physical_network'] = parsed_args.physical_network if parsed_args.segmentation_id: attrs['provider:segmentation_id'] = parsed_args.segmentation_id + # Update VLAN Transparency for networks + if parsed_args.transparent_vlan: + attrs['vlan_transparent'] = True + if parsed_args.no_transparent_vlan: + attrs['vlan_transparent'] = False return attrs -def _add_provider_network_options(parser): - # Add provider network options +def _add_additional_network_options(parser): + # Add additional network options + parser.add_argument( '--provider-network-type', metavar='<provider-network-type>', @@ -119,6 +122,16 @@ def _add_provider_network_options(parser): help=_("VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN " "networks")) + vlan_transparent_grp = parser.add_mutually_exclusive_group() + vlan_transparent_grp.add_argument( + '--transparent-vlan', + action='store_true', + help=_("Make the network VLAN transparent")) + vlan_transparent_grp.add_argument( + '--no-transparent-vlan', + action='store_true', + help=_("Do not make the network VLAN transparent")) + def _get_attrs_compute(client_manager, parsed_args): attrs = {} @@ -206,10 +219,10 @@ class CreateNetwork(common.NetworkAndComputeShowOne): default_router_grp.add_argument( '--no-default', action='store_true', - help=_("Do not use the network as the default external network. " + help=_("Do not use the network as the default external network " "(default)") ) - _add_provider_network_options(parser) + _add_additional_network_options(parser) return parser def update_parser_compute(self, parser): @@ -235,30 +248,30 @@ class CreateNetwork(common.NetworkAndComputeShowOne): return (columns, data) -class DeleteNetwork(common.NetworkAndComputeCommand): +class DeleteNetwork(common.NetworkAndComputeDelete): """Delete network(s)""" + # Used by base class to find resources in parsed_args. + resource = 'network' + r = None + def update_parser_common(self, parser): parser.add_argument( 'network', metavar="<network>", nargs="+", - help=("Network(s) to delete (name or ID)") + help=_("Network(s) to delete (name or ID)") ) + return parser def take_action_network(self, client, parsed_args): - for network in parsed_args.network: - obj = client.find_network(network) - client.delete_network(obj) + obj = client.find_network(self.r, ignore_missing=False) + client.delete_network(obj) def take_action_compute(self, client, parsed_args): - for network in parsed_args.network: - network = utils.find_resource( - client.networks, - network, - ) - client.networks.delete(network.id) + network = utils.find_resource(client.networks, self.r) + client.networks.delete(network.id) class ListNetwork(common.NetworkAndComputeLister): @@ -269,13 +282,13 @@ class ListNetwork(common.NetworkAndComputeLister): '--external', action='store_true', default=False, - help='List external networks', + help=_("List external networks") ) parser.add_argument( '--long', action='store_true', default=False, - help='List additional fields in output', + help=_("List additional fields in output") ) return parser @@ -290,7 +303,7 @@ class ListNetwork(common.NetworkAndComputeLister): 'shared', 'subnets', 'provider_network_type', - 'router_external', + 'router:external', 'availability_zones', ) column_headers = ( @@ -413,7 +426,7 @@ class SetNetwork(command.Command): action='store_true', help=_("Do not use the network as the default external network") ) - _add_provider_network_options(parser) + _add_additional_network_options(parser) return parser def take_action(self, parsed_args): @@ -422,7 +435,7 @@ class SetNetwork(command.Command): attrs = _get_attrs(self.app.client_manager, parsed_args) if attrs == {}: - msg = "Nothing specified to be set" + msg = _("Nothing specified to be set") raise exceptions.CommandError(msg) client.update_network(obj, **attrs) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 9b6161fd..aa0894f8 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -414,7 +414,7 @@ class SetPort(command.Command): attrs['fixed_ips'] = [] if attrs == {}: - msg = "Nothing specified to be set" + msg = _("Nothing specified to be set") raise exceptions.CommandError(msg) client.update_port(obj, **attrs) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index a32ab5ea..a2f0df1d 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -13,7 +13,9 @@ """Router action implementations""" +import argparse import json +import logging from openstackclient.common import command from openstackclient.common import exceptions @@ -23,6 +25,9 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +LOG = logging.getLogger(__name__) + + def _format_admin_state(state): return 'UP' if state else 'DOWN' @@ -379,10 +384,15 @@ class SetRouter(command.Command): "(repeat option to set multiple routes)") ) routes_group.add_argument( - '--clear-routes', + '--no-route', action='store_true', help=_("Clear routes associated with the router") ) + routes_group.add_argument( + '--clear-routes', + action='store_true', + help=argparse.SUPPRESS, + ) # TODO(tangchen): Support setting 'ha' property in 'router set' # command. It appears that changing the ha state is supported by @@ -401,8 +411,14 @@ class SetRouter(command.Command): attrs = _get_attrs(self.app.client_manager, parsed_args) # Get the route attributes. - if parsed_args.clear_routes: + if parsed_args.no_route: + attrs['routes'] = [] + elif parsed_args.clear_routes: attrs['routes'] = [] + LOG.warning(_( + 'The --clear-routes option is deprecated, ' + 'please use --no-route instead.' + )) elif parsed_args.routes is not None: # Map the route keys and append to the current routes. # The REST API will handle route validation and duplicates. @@ -411,7 +427,7 @@ class SetRouter(command.Command): attrs['routes'] = obj.routes + parsed_args.routes if attrs == {}: - msg = "Nothing specified to be set" + msg = _("Nothing specified to be set") raise exceptions.CommandError(msg) client.update_router(obj, **attrs) diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 5b22a0dd..5abe9b9d 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -36,9 +36,21 @@ def _format_security_group_rule_show(obj): def _format_network_port_range(rule): + # Display port range or ICMP type and code. For example: + # - ICMP type: 'type=3' + # - ICMP type and code: 'type=3:code=0' + # - ICMP code: Not supported + # - Matching port range: '443:443' + # - Different port range: '22:24' + # - Single port: '80:80' + # - No port range: '' port_range = '' - if (rule.protocol != 'icmp' and - (rule.port_range_min or rule.port_range_max)): + if _is_icmp_protocol(rule.protocol): + if rule.port_range_min: + port_range += 'type=' + str(rule.port_range_min) + if rule.port_range_max: + port_range += ':code=' + str(rule.port_range_max) + elif rule.port_range_min or rule.port_range_max: port_range_min = str(rule.port_range_min) port_range_max = str(rule.port_range_max) if rule.port_range_min is None: @@ -61,6 +73,17 @@ def _convert_to_lowercase(string): return string.lower() +def _is_icmp_protocol(protocol): + # NOTE(rtheis): Neutron has deprecated protocol icmpv6. + # However, while the OSC CLI doesn't document the protocol, + # the code must still handle it. In addition, handle both + # protocol names and numbers. + if protocol in ['icmp', 'icmpv6', 'ipv6-icmp', '1', '58']: + return True + else: + return False + + class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): """Create a new security group rule""" @@ -68,19 +91,7 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): 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. When additional - # protocols are added, the default ethertype must be determined - # based on the protocol. - parser.add_argument( - "--proto", - metavar="<proto>", - default="tcp", - choices=['icmp', 'tcp', 'udp'], - type=_convert_to_lowercase, - help=_("IP protocol (icmp, tcp, udp; default: tcp)") + help=_("Create rule in this security group (name or ID)") ) source_group = parser.add_mutually_exclusive_group() source_group.add_argument( @@ -94,17 +105,49 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): 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 update_parser_network(self, parser): + parser.add_argument( + '--dst-port', + metavar='<port-range>', + action=parseractions.RangeAction, + help=_("Destination port, may be a single port or a starting and " + "ending port range: 137:139. Required for IP protocols TCP " + "and UDP. Ignored for ICMP IP protocols.") + ) + parser.add_argument( + '--icmp-type', + metavar='<icmp-type>', + type=int, + help=_("ICMP type for ICMP IP protocols") + ) + parser.add_argument( + '--icmp-code', + metavar='<icmp-code>', + type=int, + help=_("ICMP code for ICMP IP protocols") + ) + # NOTE(rtheis): Support either protocol option name for now. + # However, consider deprecating and then removing --proto in + # a future release. + protocol_group = parser.add_mutually_exclusive_group() + protocol_group.add_argument( + '--protocol', + metavar='<protocol>', + type=_convert_to_lowercase, + help=_("IP protocol (ah, dccp, egp, esp, gre, icmp, igmp, " + "ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, " + "ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, " + "udp, udplite, vrrp and integer representations [0-255]; " + "default: tcp)") + ) + protocol_group.add_argument( + '--proto', + metavar='<proto>', + type=_convert_to_lowercase, + help=argparse.SUPPRESS + ) direction_group = parser.add_mutually_exclusive_group() direction_group.add_argument( '--ingress', @@ -120,7 +163,8 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): '--ethertype', metavar='<ethertype>', choices=['IPv4', 'IPv6'], - help=_("Ethertype of network traffic (IPv4, IPv6; default: IPv4)") + help=_("Ethertype of network traffic " + "(IPv4, IPv6; default: based on IP protocol)") ) parser.add_argument( '--project', @@ -130,6 +174,55 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): identity_common.add_project_domain_option_to_parser(parser) return parser + def update_parser_compute(self, parser): + parser.add_argument( + '--dst-port', + metavar='<port-range>', + default=(0, 0), + action=parseractions.RangeAction, + help=_("Destination port, may be a single port or a starting and " + "ending port range: 137:139. Required for IP protocols TCP " + "and UDP. Ignored for ICMP IP protocols.") + ) + # NOTE(rtheis): Support either protocol option name for now. + # However, consider deprecating and then removing --proto in + # a future release. + protocol_group = parser.add_mutually_exclusive_group() + protocol_group.add_argument( + '--protocol', + metavar='<protocol>', + choices=['icmp', 'tcp', 'udp'], + type=_convert_to_lowercase, + help=_("IP protocol (icmp, tcp, udp; default: tcp)") + ) + protocol_group.add_argument( + '--proto', + metavar='<proto>', + choices=['icmp', 'tcp', 'udp'], + type=_convert_to_lowercase, + help=argparse.SUPPRESS + ) + return parser + + def _get_protocol(self, parsed_args): + protocol = 'tcp' + if parsed_args.protocol is not None: + protocol = parsed_args.protocol + if parsed_args.proto is not None: + protocol = parsed_args.proto + return protocol + + def _is_ipv6_protocol(self, protocol): + # NOTE(rtheis): Neutron has deprecated protocol icmpv6. + # However, while the OSC CLI doesn't document the protocol, + # the code must still handle it. In addition, handle both + # protocol names and numbers. + if (protocol.startswith('ipv6-') or + protocol in ['icmpv6', '41', '43', '44', '58', '59', '60']): + return True + else: + return False + def take_action_network(self, client, parsed_args): # Get the security group ID to hold the rule. security_group_id = client.find_security_group( @@ -139,24 +232,50 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): # Build the create attributes. attrs = {} + attrs['protocol'] = self._get_protocol(parsed_args) + # NOTE(rtheis): A direction must be specified and ingress # is the default. if parsed_args.ingress or not parsed_args.egress: attrs['direction'] = 'ingress' if parsed_args.egress: attrs['direction'] = 'egress' + + # NOTE(rtheis): Use ethertype specified else default based + # on IP protocol. if parsed_args.ethertype: attrs['ethertype'] = parsed_args.ethertype + elif self._is_ipv6_protocol(attrs['protocol']): + attrs['ethertype'] = 'IPv6' else: - # NOTE(rtheis): Default based on protocol is IPv4 for now. - # Once IPv6 protocols are added, this will need to be updated. 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': + + # NOTE(rtheis): Validate the port range and ICMP type and code. + # It would be ideal if argparse could do this. + if parsed_args.dst_port and (parsed_args.icmp_type or + parsed_args.icmp_code): + msg = _('Argument --dst-port not allowed with arguments ' + '--icmp-type and --icmp-code') + raise exceptions.CommandError(msg) + if parsed_args.icmp_type is None and parsed_args.icmp_code is not None: + msg = _('Argument --icmp-type required with argument --icmp-code') + raise exceptions.CommandError(msg) + is_icmp_protocol = _is_icmp_protocol(attrs['protocol']) + if not is_icmp_protocol and (parsed_args.icmp_type or + parsed_args.icmp_code): + msg = _('ICMP IP protocol required with arguments ' + '--icmp-type and --icmp-code') + raise exceptions.CommandError(msg) + # NOTE(rtheis): For backwards compatibility, continue ignoring + # the destination port range when an ICMP IP protocol is specified. + if parsed_args.dst_port and not is_icmp_protocol: 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.icmp_type: + attrs['port_range_min'] = parsed_args.icmp_type + if parsed_args.icmp_code: + attrs['port_range_max'] = parsed_args.icmp_code + if parsed_args.src_group is not None: attrs['remote_group_id'] = client.find_security_group( parsed_args.src_group, @@ -187,7 +306,8 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): client.security_groups, parsed_args.group, ) - if parsed_args.proto == 'icmp': + protocol = self._get_protocol(parsed_args) + if protocol == 'icmp': from_port, to_port = -1, -1 else: from_port, to_port = parsed_args.dst_port @@ -203,7 +323,7 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): src_ip = '0.0.0.0/0' obj = client.security_group_rules.create( group.id, - parsed_args.proto, + protocol, from_port, to_port, src_ip, @@ -399,8 +519,8 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne): break if obj is None: - msg = "Could not find security group rule " \ - "with ID %s" % parsed_args.rule + msg = _("Could not find security group rule with ID ") + \ + parsed_args.rule raise exceptions.CommandError(msg) # NOTE(rtheis): Format security group rule diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index fb441cbf..f51aec5b 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -141,9 +141,9 @@ def _get_attrs(client_manager, parsed_args, is_create=True): 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") + msg = _("Auto option is not available for Subnet Set. " + "Valid options are <ip-address> or none") + raise exceptions.CommandError(msg) elif gateway != 'auto': if gateway == 'none': attrs['gateway_ip'] = None diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index f1174dda..a1a94426 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -287,7 +287,7 @@ class SetSubnetPool(command.Command): attrs = _get_attrs(self.app.client_manager, parsed_args) if attrs == {}: - msg = "Nothing specified to be set" + msg = _("Nothing specified to be set") raise exceptions.CommandError(msg) # Existing prefixes must be a subset of the new prefixes. |
