summaryrefslogtreecommitdiff
path: root/openstackclient/network
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient/network')
-rw-r--r--openstackclient/network/common.py37
-rw-r--r--openstackclient/network/v2/address_scope.py2
-rw-r--r--openstackclient/network/v2/network.py61
-rw-r--r--openstackclient/network/v2/port.py2
-rw-r--r--openstackclient/network/v2/router.py22
-rw-r--r--openstackclient/network/v2/security_group_rule.py188
-rw-r--r--openstackclient/network/v2/subnet.py6
-rw-r--r--openstackclient/network/v2/subnet_pool.py2
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.