summaryrefslogtreecommitdiff
path: root/openstackclient/network/v2
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient/network/v2')
-rw-r--r--openstackclient/network/v2/floating_ip.py88
-rw-r--r--openstackclient/network/v2/network.py160
-rw-r--r--openstackclient/network/v2/port.py145
-rw-r--r--openstackclient/network/v2/router.py176
-rw-r--r--openstackclient/network/v2/security_group.py19
-rw-r--r--openstackclient/network/v2/security_group_rule.py317
-rw-r--r--openstackclient/network/v2/subnet.py283
-rw-r--r--openstackclient/network/v2/subnet_pool.py89
8 files changed, 1015 insertions, 262 deletions
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index 16f2b574..21f86599 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -14,6 +14,7 @@
"""IP Floating action implementations"""
from openstackclient.common import utils
+from openstackclient.i18n import _
from openstackclient.network import common
@@ -25,6 +26,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"""
@@ -32,7 +116,7 @@ class DeleteFloatingIP(common.NetworkAndComputeCommand):
parser.add_argument(
'floating_ip',
metavar="<floating-ip>",
- help=("Floating IP to delete (IP address or ID)")
+ help=_("Floating IP to delete (IP address or ID)")
)
return parser
@@ -106,7 +190,7 @@ class ShowFloatingIP(common.NetworkAndComputeShowOne):
parser.add_argument(
'floating_ip',
metavar="<floating-ip>",
- help=("Floating IP to display (IP address or ID)")
+ help=_("Floating IP to display (IP address or ID)")
)
return parser
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 074b2754..4b77971a 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -16,6 +16,7 @@
from openstackclient.common import command
from openstackclient.common import exceptions
from openstackclient.common import utils
+from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import common
@@ -52,10 +53,14 @@ def _get_attrs(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
- if parsed_args.admin_state is not None:
- attrs['admin_state_up'] = parsed_args.admin_state
- if parsed_args.shared is not None:
- attrs['shared'] = parsed_args.shared
+ if parsed_args.enable:
+ attrs['admin_state_up'] = True
+ if parsed_args.disable:
+ attrs['admin_state_up'] = False
+ if parsed_args.share:
+ attrs['shared'] = True
+ if parsed_args.no_share:
+ attrs['shared'] = False
# "network set" command doesn't support setting project.
if 'project' in parsed_args and parsed_args.project is not None:
@@ -72,18 +77,59 @@ def _get_attrs(client_manager, parsed_args):
parsed_args.availability_zone_hints is not None:
attrs['availability_zone_hints'] = parsed_args.availability_zone_hints
+ # update_external_network_options
+ if parsed_args.internal:
+ attrs['router:external'] = False
+ if parsed_args.external:
+ attrs['router:external'] = True
+ if parsed_args.no_default:
+ attrs['is_default'] = False
+ if parsed_args.default:
+ attrs['is_default'] = True
+ # Update Provider network options
+ if parsed_args.provider_network_type:
+ attrs['provider:network_type'] = parsed_args.provider_network_type
+ if parsed_args.physical_network:
+ attrs['provider:physical_network'] = parsed_args.physical_network
+ if parsed_args.segmentation_id:
+ attrs['provider:segmentation_id'] = parsed_args.segmentation_id
return attrs
+def _add_provider_network_options(parser):
+ # Add provider network options
+ parser.add_argument(
+ '--provider-network-type',
+ metavar='<provider-network-type>',
+ choices=['flat', 'gre', 'local',
+ 'vlan', 'vxlan'],
+ help=_("The physical mechanism by which the virtual network "
+ "is implemented. The supported options are: "
+ "flat, gre, local, vlan, vxlan"))
+ parser.add_argument(
+ '--provider-physical-network',
+ metavar='<provider-physical-network>',
+ dest='physical_network',
+ help=_("Name of the physical network over which the virtual "
+ "network is implemented"))
+ parser.add_argument(
+ '--provider-segment',
+ metavar='<provider-segment>',
+ dest='segmentation_id',
+ help=_("VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN "
+ "networks"))
+
+
def _get_attrs_compute(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
attrs['label'] = str(parsed_args.name)
- if parsed_args.shared is not None:
- attrs['share_address'] = parsed_args.shared
+ if parsed_args.share:
+ attrs['share_address'] = True
+ if parsed_args.no_share:
+ attrs['share_address'] = False
if parsed_args.subnet is not None:
attrs['cidr'] = parsed_args.subnet
-
return attrs
@@ -94,21 +140,19 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help='New network name',
+ help=_("New network name")
)
share_group = parser.add_mutually_exclusive_group()
share_group.add_argument(
'--share',
- dest='shared',
action='store_true',
default=None,
- help='Share the network between projects',
+ help=_("Share the network between projects")
)
share_group.add_argument(
'--no-share',
- dest='shared',
- action='store_false',
- help='Do not share the network between projects',
+ action='store_true',
+ help=_("Do not share the network between projects")
)
return parser
@@ -116,21 +160,19 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state',
action='store_true',
default=True,
- help='Enable network (default)',
+ help=_("Enable network (default)")
)
admin_group.add_argument(
'--disable',
- dest='admin_state',
- action='store_false',
- help='Disable network',
+ action='store_true',
+ help=_("Disable network")
)
parser.add_argument(
'--project',
metavar='<project>',
- help="Owner's project (name or ID)"
+ help=_("Owner's project (name or ID)")
)
identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
@@ -138,17 +180,43 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
action='append',
dest='availability_zone_hints',
metavar='<availability-zone>',
- help='Availability Zone in which to create this network '
- '(requires the Network Availability Zone extension, '
- 'this option can be repeated).',
+ help=_("Availability Zone in which to create this network "
+ "(Network Availability Zone extension required, "
+ "repeat option to set multiple availability zones)")
)
+ external_router_grp = parser.add_mutually_exclusive_group()
+ external_router_grp.add_argument(
+ '--external',
+ action='store_true',
+ help=_("Set this network as an external network "
+ "(external-net extension required)")
+ )
+ external_router_grp.add_argument(
+ '--internal',
+ action='store_true',
+ help=_("Set this network as an internal network (default)")
+ )
+ default_router_grp = parser.add_mutually_exclusive_group()
+ default_router_grp.add_argument(
+ '--default',
+ action='store_true',
+ help=_("Specify if this network should be used as "
+ "the default external network")
+ )
+ default_router_grp.add_argument(
+ '--no-default',
+ action='store_true',
+ help=_("Do not use the network as the default external network. "
+ "(default)")
+ )
+ _add_provider_network_options(parser)
return parser
def update_parser_compute(self, parser):
parser.add_argument(
'--subnet',
metavar='<subnet>',
- help="IPv4 subnet for fixed IPs (in CIDR notation)"
+ help=_("IPv4 subnet for fixed IPs (in CIDR notation)")
)
return parser
@@ -291,41 +359,61 @@ class SetNetwork(command.Command):
parser.add_argument(
'network',
metavar="<network>",
- help=("Network to modify (name or ID)")
+ help=_("Network to modify (name or ID)")
)
parser.add_argument(
'--name',
metavar='<name>',
- help='Set network name',
+ help=_("Set network name")
)
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state',
action='store_true',
default=None,
- help='Enable network',
+ help=_("Enable network")
)
admin_group.add_argument(
'--disable',
- dest='admin_state',
- action='store_false',
- help='Disable network',
+ action='store_true',
+ help=_("Disable network")
)
share_group = parser.add_mutually_exclusive_group()
share_group.add_argument(
'--share',
- dest='shared',
action='store_true',
default=None,
- help='Share the network between projects',
+ help=_("Share the network between projects")
)
share_group.add_argument(
'--no-share',
- dest='shared',
- action='store_false',
- help='Do not share the network between projects',
+ action='store_true',
+ help=_("Do not share the network between projects")
+ )
+ external_router_grp = parser.add_mutually_exclusive_group()
+ external_router_grp.add_argument(
+ '--external',
+ action='store_true',
+ help=_("Set this network as an external network "
+ "(external-net extension required)")
+ )
+ external_router_grp.add_argument(
+ '--internal',
+ action='store_true',
+ help=_("Set this network as an internal network")
+ )
+ default_router_grp = parser.add_mutually_exclusive_group()
+ default_router_grp.add_argument(
+ '--default',
+ action='store_true',
+ help=_("Set the network as the default external network")
+ )
+ default_router_grp.add_argument(
+ '--no-default',
+ action='store_true',
+ help=_("Do not use the network as the default external network")
)
+ _add_provider_network_options(parser)
return parser
def take_action(self, parsed_args):
@@ -347,7 +435,7 @@ class ShowNetwork(common.NetworkAndComputeShowOne):
parser.add_argument(
'network',
metavar="<network>",
- help=("Network to display (name or ID)")
+ help=_("Network to display (name or ID)")
)
return parser
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 23350cf8..9b6161fd 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -30,6 +30,7 @@ LOG = logging.getLogger(__name__)
def _format_admin_state(state):
return 'UP' if state else 'DOWN'
+
_formatters = {
'admin_state_up': _format_admin_state,
'allowed_address_pairs': utils.format_list_of_dicts,
@@ -86,8 +87,10 @@ def _get_attrs(client_manager, parsed_args):
attrs['device_id'] = parsed_args.device
if parsed_args.device_owner is not None:
attrs['device_owner'] = parsed_args.device_owner
- if parsed_args.admin_state is not None:
- attrs['admin_state_up'] = parsed_args.admin_state
+ if parsed_args.enable:
+ attrs['admin_state_up'] = True
+ if parsed_args.disable:
+ attrs['admin_state_up'] = False
if parsed_args.binding_profile is not None:
attrs['binding:profile'] = parsed_args.binding_profile
if parsed_args.vnic_type is not None:
@@ -95,10 +98,11 @@ def _get_attrs(client_manager, parsed_args):
if parsed_args.host:
attrs['binding:host_id'] = parsed_args.host
+ # It is possible that name is not updated during 'port set'
+ if parsed_args.name is not None:
+ attrs['name'] = str(parsed_args.name)
# The remaining options do not support 'port set' command, so they require
# additional check
- if 'name' in parsed_args and parsed_args.name is not None:
- attrs['name'] = str(parsed_args.name)
if 'mac_address' in parsed_args and parsed_args.mac_address is not None:
attrs['mac_address'] = parsed_args.mac_address
if 'network' in parsed_args and parsed_args.network is not None:
@@ -148,21 +152,13 @@ def _prepare_fixed_ips(client_manager, parsed_args):
def _add_updatable_args(parser):
- parser.add_argument(
- '--fixed-ip',
- metavar='subnet=<subnet>,ip-address=<ip-address>',
- action=parseractions.MultiKeyValueAction,
- optional_keys=['subnet', 'ip-address'],
- help='Desired IP and/or subnet (name or ID) for this port: '
- 'subnet=<subnet>,ip-address=<ip-address> '
- '(this option can be repeated)')
# NOTE(dtroyer): --device-id is deprecated in Mar 2016. Do not
# remove before 3.x release or Mar 2017.
device_group = parser.add_mutually_exclusive_group()
device_group.add_argument(
'--device',
metavar='<device-id>',
- help='Port device ID',
+ help=_("Port device ID")
)
device_group.add_argument(
'--device-id',
@@ -172,28 +168,23 @@ def _add_updatable_args(parser):
parser.add_argument(
'--device-owner',
metavar='<device-owner>',
- help='Device owner of this port')
+ help=_("Device owner of this port")
+ )
parser.add_argument(
'--vnic-type',
metavar='<vnic-type>',
choices=['direct', 'direct-physical', 'macvtap',
'normal', 'baremetal'],
- help="VNIC type for this port (direct | direct-physical |"
- " macvtap | normal | baremetal). If unspecified during"
- " port creation, default value will be 'normal'.")
- parser.add_argument(
- '--binding-profile',
- metavar='<binding-profile>',
- action=parseractions.KeyValueAction,
- help='Custom data to be passed as binding:profile: <key>=<value> '
- '(this option can be repeated)')
+ help=_("VNIC type for this port (direct | direct-physical | "
+ "macvtap | normal | baremetal, default: normal)")
+ )
# NOTE(dtroyer): --host-id is deprecated in Mar 2016. Do not
# remove before 3.x release or Mar 2017.
host_group = parser.add_mutually_exclusive_group()
host_group.add_argument(
'--host',
metavar='<host-id>',
- help='Allocate port on host <host-id> (ID only)',
+ help=_("Allocate port on host <host-id> (ID only)")
)
host_group.add_argument(
'--host-id',
@@ -212,35 +203,54 @@ class CreatePort(command.ShowOne):
'--network',
metavar='<network>',
required=True,
- help='Network this port belongs to (name or ID)')
+ help=_("Network this port belongs to (name or ID)")
+ )
_add_updatable_args(parser)
+ parser.add_argument(
+ '--fixed-ip',
+ metavar='subnet=<subnet>,ip-address=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ optional_keys=['subnet', 'ip-address'],
+ help=_("Desired IP and/or subnet (name or ID) for this port: "
+ "subnet=<subnet>,ip-address=<ip-address> "
+ "(repeat option to set multiple fixed IP addresses)")
+ )
+ parser.add_argument(
+ '--binding-profile',
+ metavar='<binding-profile>',
+ action=parseractions.KeyValueAction,
+ help=_("Custom data to be passed as binding:profile: "
+ "<key>=<value> "
+ "(repeat option to set multiple binding:profile data)")
+ )
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state',
action='store_true',
default=True,
- help='Enable port (default)',
+ help=_("Enable port (default)")
)
admin_group.add_argument(
'--disable',
- dest='admin_state',
- action='store_false',
- help='Disable port',
+ action='store_true',
+ help=_("Disable port")
)
parser.add_argument(
'--mac-address',
metavar='<mac-address>',
- help='MAC address of this port')
+ help=_("MAC address of this port")
+ )
parser.add_argument(
'--project',
metavar='<project>',
- help="Owner's project (name or ID)")
+ help=_("Owner's project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'name',
metavar='<name>',
- help='Name of this port')
- identity_common.add_project_domain_option_to_parser(parser)
+ help=_("Name of this port")
+ )
# TODO(singhj): Add support for extended options:
# qos,security groups,dhcp, address pairs
return parser
@@ -268,7 +278,7 @@ class DeletePort(command.Command):
'port',
metavar="<port>",
nargs="+",
- help=("Port(s) to delete (name or ID)")
+ help=_("Port(s) to delete (name or ID)")
)
return parser
@@ -289,7 +299,7 @@ class ListPort(command.Lister):
'--router',
metavar='<router>',
dest='router',
- help='List only ports attached to this router (name or ID)',
+ help=_("List only ports attached to this router (name or ID)")
)
return parser
@@ -333,23 +343,54 @@ class SetPort(command.Command):
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state',
action='store_true',
default=None,
- help='Enable port',
+ help=_("Enable port")
)
admin_group.add_argument(
'--disable',
- dest='admin_state',
- action='store_false',
- help='Disable port',
+ action='store_true',
+ help=_("Disable port")
+ )
+ parser.add_argument(
+ '--name',
+ metavar="<name>",
+ help=_("Set port name")
+ )
+ fixed_ip = parser.add_mutually_exclusive_group()
+ fixed_ip.add_argument(
+ '--fixed-ip',
+ metavar='subnet=<subnet>,ip-address=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ optional_keys=['subnet', 'ip-address'],
+ help=_("Desired IP and/or subnet (name or ID) for this port: "
+ "subnet=<subnet>,ip-address=<ip-address> "
+ "(repeat option to set multiple fixed IP addresses)")
+ )
+ fixed_ip.add_argument(
+ '--no-fixed-ip',
+ action='store_true',
+ help=_("Clear existing information of fixed IP addresses")
+ )
+ binding_profile = parser.add_mutually_exclusive_group()
+ binding_profile.add_argument(
+ '--binding-profile',
+ metavar='<binding-profile>',
+ action=parseractions.KeyValueAction,
+ help=_("Custom data to be passed as binding:profile: "
+ "<key>=<value> "
+ "(repeat option to set multiple binding:profile data)")
+ )
+ binding_profile.add_argument(
+ '--no-binding-profile',
+ action='store_true',
+ help=_("Clear existing information of binding:profile")
)
parser.add_argument(
'port',
metavar="<port>",
- help=("Port to modify (name or ID)")
+ help=_("Port to modify (name or ID)")
)
-
return parser
def take_action(self, parsed_args):
@@ -357,12 +398,24 @@ class SetPort(command.Command):
_prepare_fixed_ips(self.app.client_manager, parsed_args)
attrs = _get_attrs(self.app.client_manager, parsed_args)
+ obj = client.find_port(parsed_args.port, ignore_missing=False)
+ if 'binding:profile' in attrs:
+ attrs['binding:profile'].update(obj.binding_profile)
+ elif parsed_args.no_binding_profile:
+ attrs['binding:profile'] = {}
+ if 'fixed_ips' in attrs:
+ # When user unsets the fixed_ips, obj.fixed_ips = [{}].
+ # Adding the obj.fixed_ips list to attrs['fixed_ips']
+ # would therefore add an empty dictionary, while we need
+ # to append the attrs['fixed_ips'] iff there is some info
+ # in the obj.fixed_ips. Therefore I have opted for this `for` loop
+ attrs['fixed_ips'] += [ip for ip in obj.fixed_ips if ip]
+ elif parsed_args.no_fixed_ip:
+ attrs['fixed_ips'] = []
if attrs == {}:
msg = "Nothing specified to be set"
raise exceptions.CommandError(msg)
-
- obj = client.find_port(parsed_args.port, ignore_missing=False)
client.update_port(obj, **attrs)
@@ -374,7 +427,7 @@ class ShowPort(command.ShowOne):
parser.add_argument(
'port',
metavar="<port>",
- help="Port to display (name or ID)"
+ help=_("Port to display (name or ID)")
)
return parser
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 39431111..56630a23 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -19,6 +19,7 @@ from openstackclient.common import command
from openstackclient.common import exceptions
from openstackclient.common import parseractions
from openstackclient.common import utils
+from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
@@ -53,10 +54,15 @@ def _get_attrs(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
- if parsed_args.admin_state_up is not None:
- attrs['admin_state_up'] = parsed_args.admin_state_up
- if parsed_args.distributed is not None:
- attrs['distributed'] = parsed_args.distributed
+ if parsed_args.enable:
+ attrs['admin_state_up'] = True
+ if parsed_args.disable:
+ attrs['admin_state_up'] = False
+ # centralized is available only for SetRouter and not for CreateRouter
+ if 'centralized' in parsed_args and parsed_args.centralized:
+ attrs['distributed'] = False
+ if parsed_args.distributed:
+ attrs['distributed'] = True
if ('availability_zone_hints' in parsed_args
and parsed_args.availability_zone_hints is not None):
attrs['availability_zone_hints'] = parsed_args.availability_zone_hints
@@ -82,6 +88,57 @@ def _get_attrs(client_manager, parsed_args):
return attrs
+class AddPortToRouter(command.Command):
+ """Add a port to a router"""
+
+ def get_parser(self, prog_name):
+ parser = super(AddPortToRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router to which port will be added (name or ID)")
+ )
+ parser.add_argument(
+ 'port',
+ metavar='<port>',
+ help=_("Port to be added (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ port = client.find_port(parsed_args.port, ignore_missing=False)
+ client.router_add_interface(client.find_router(
+ parsed_args.router, ignore_missing=False), port_id=port.id)
+
+
+class AddSubnetToRouter(command.Command):
+ """Add a subnet to a router"""
+
+ def get_parser(self, prog_name):
+ parser = super(AddSubnetToRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router to which subnet will be added (name or ID)")
+ )
+ parser.add_argument(
+ 'subnet',
+ metavar='<subnet>',
+ help=_("Subnet to be added (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ subnet = client.find_subnet(parsed_args.subnet,
+ ignore_missing=False)
+ client.router_add_interface(
+ client.find_router(parsed_args.router,
+ ignore_missing=False),
+ subnet_id=subnet.id)
+
+
class CreateRouter(command.ShowOne):
"""Create a new router"""
@@ -90,45 +147,43 @@ class CreateRouter(command.ShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help="New router name",
+ help=_("New router name")
)
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state_up',
action='store_true',
default=True,
- help="Enable router (default)",
+ help=_("Enable router (default)")
)
admin_group.add_argument(
'--disable',
- dest='admin_state_up',
- action='store_false',
- help="Disable router",
+ action='store_true',
+ help=_("Disable router")
)
parser.add_argument(
'--distributed',
dest='distributed',
action='store_true',
default=False,
- help="Create a distributed router",
+ help=_("Create a distributed router")
)
parser.add_argument(
'--project',
metavar='<project>',
- help="Owner's project (name or ID)",
+ help=_("Owner's project (name or ID)")
)
+ identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--availability-zone-hint',
metavar='<availability-zone>',
action='append',
dest='availability_zone_hints',
- help='Availability Zone in which to create this router '
- '(requires the Router Availability Zone extension, '
- 'this option can be repeated).',
+ help=_("Availability Zone in which to create this router "
+ "(Router Availability Zone extension required, "
+ "repeat option to set multiple availability zones)")
)
- identity_common.add_project_domain_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -152,7 +207,7 @@ class DeleteRouter(command.Command):
'router',
metavar="<router>",
nargs="+",
- help=("Router(s) to delete (name or ID)")
+ help=_("Router(s) to delete (name or ID)")
)
return parser
@@ -172,7 +227,7 @@ class ListRouter(command.Lister):
'--long',
action='store_true',
default=False,
- help='List additional fields in output',
+ help=_("List additional fields in output")
)
return parser
@@ -217,6 +272,57 @@ class ListRouter(command.Lister):
) for s in data))
+class RemovePortFromRouter(command.Command):
+ """Remove a port from a router"""
+
+ def get_parser(self, prog_name):
+ parser = super(RemovePortFromRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router from which port will be removed (name or ID)")
+ )
+ parser.add_argument(
+ 'port',
+ metavar='<port>',
+ help=_("Port to be removed (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ port = client.find_port(parsed_args.port, ignore_missing=False)
+ client.router_remove_interface(client.find_router(
+ parsed_args.router, ignore_missing=False), port_id=port.id)
+
+
+class RemoveSubnetFromRouter(command.Command):
+ """Remove a subnet from a router"""
+
+ def get_parser(self, prog_name):
+ parser = super(RemoveSubnetFromRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router from which the subnet will be removed (name or ID)")
+ )
+ parser.add_argument(
+ 'subnet',
+ metavar='<subnet>',
+ help=_("Subnet to be removed (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ subnet = client.find_subnet(parsed_args.subnet,
+ ignore_missing=False)
+ client.router_remove_interface(
+ client.find_router(parsed_args.router,
+ ignore_missing=False),
+ subnet_id=subnet.id)
+
+
class SetRouter(command.Command):
"""Set router properties"""
@@ -225,40 +331,35 @@ class SetRouter(command.Command):
parser.add_argument(
'router',
metavar="<router>",
- help=("Router to modify (name or ID)")
+ help=_("Router to modify (name or ID)")
)
parser.add_argument(
'--name',
metavar='<name>',
- help='Set router name',
+ help=_("Set router name")
)
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state_up',
action='store_true',
default=None,
- help='Enable router',
+ help=_("Enable router")
)
admin_group.add_argument(
'--disable',
- dest='admin_state_up',
- action='store_false',
- help='Disable router',
+ action='store_true',
+ help=_("Disable router")
)
distribute_group = parser.add_mutually_exclusive_group()
distribute_group.add_argument(
'--distributed',
- dest='distributed',
action='store_true',
- default=None,
- help="Set router to distributed mode (disabled router only)",
+ help=_("Set router to distributed mode (disabled router only)")
)
distribute_group.add_argument(
'--centralized',
- dest='distributed',
- action='store_false',
- help="Set router to centralized mode (disabled router only)",
+ action='store_true',
+ help=_("Set router to centralized mode (disabled router only)")
)
routes_group = parser.add_mutually_exclusive_group()
routes_group.add_argument(
@@ -268,16 +369,15 @@ class SetRouter(command.Command):
dest='routes',
default=None,
required_keys=['destination', 'gateway'],
- help="Routes associated with the router. "
- "Repeat this option to set multiple routes. "
- "destination: destination subnet (in CIDR notation). "
- "gateway: nexthop IP address.",
+ help=_("Routes associated with the router "
+ "destination: destination subnet (in CIDR notation) "
+ "gateway: nexthop IP address "
+ "(repeat option to set multiple routes)")
)
routes_group.add_argument(
'--clear-routes',
- dest='clear_routes',
action='store_true',
- help="Clear routes associated with the router",
+ help=_("Clear routes associated with the router")
)
# TODO(tangchen): Support setting 'ha' property in 'router set'
@@ -309,7 +409,7 @@ class ShowRouter(command.ShowOne):
parser.add_argument(
'router',
metavar="<router>",
- help="Router to display (name or ID)"
+ help=_("Router to display (name or ID)")
)
return parser
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 92498144..1ef2754e 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -17,6 +17,7 @@ import argparse
import six
from openstackclient.common import utils
+from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import common
from openstackclient.network import utils as network_utils
@@ -99,12 +100,12 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
parser.add_argument(
"name",
metavar="<name>",
- help="New security group name",
+ help=_("New security group name")
)
parser.add_argument(
"--description",
metavar="<description>",
- help="Security group description",
+ help=_("Security group description")
)
return parser
@@ -112,7 +113,7 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
parser.add_argument(
'--project',
metavar='<project>',
- help="Owner's project (name or ID)"
+ help=_("Owner's project (name or ID)")
)
identity_common.add_project_domain_option_to_parser(parser)
return parser
@@ -169,7 +170,7 @@ class DeleteSecurityGroup(common.NetworkAndComputeCommand):
parser.add_argument(
'group',
metavar='<group>',
- help='Security group to delete (name or ID)',
+ help=_("Security group to delete (name or ID)")
)
return parser
@@ -204,7 +205,7 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
'--all-projects',
action='store_true',
default=False,
- help='Display information from all projects (admin only)',
+ help=_("Display information from all projects (admin only)")
)
return parser
@@ -240,17 +241,17 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
parser.add_argument(
'group',
metavar='<group>',
- help='Security group to modify (name or ID)',
+ help=_("Security group to modify (name or ID)")
)
parser.add_argument(
'--name',
metavar='<new-name>',
- help='New security group name',
+ help=_("New security group name")
)
parser.add_argument(
"--description",
metavar="<description>",
- help="New security group description",
+ help=_("New security group description")
)
return parser
@@ -295,7 +296,7 @@ class ShowSecurityGroup(common.NetworkAndComputeShowOne):
parser.add_argument(
'group',
metavar='<group>',
- help='Security group to display (name or ID)',
+ help=_("Security group to display (name or ID)")
)
return parser
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index 9309b326..5b22a0dd 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -13,10 +13,19 @@
"""Security Group Rule action implementations"""
+import argparse
import six
+try:
+ from novaclient.v2 import security_group_rules as compute_secgroup_rules
+except ImportError:
+ from novaclient.v1_1 import security_group_rules as compute_secgroup_rules
+
from openstackclient.common import exceptions
+from openstackclient.common import parseractions
from openstackclient.common import utils
+from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
from openstackclient.network import common
from openstackclient.network import utils as network_utils
@@ -26,6 +35,20 @@ def _format_security_group_rule_show(obj):
return zip(*sorted(six.iteritems(data)))
+def _format_network_port_range(rule):
+ port_range = ''
+ if (rule.protocol != 'icmp' and
+ (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:
+ port_range_min = port_range_max
+ if rule.port_range_max is None:
+ port_range_max = port_range_min
+ port_range = port_range_min + ':' + port_range_max
+ return port_range
+
+
def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
@@ -34,6 +57,161 @@ 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. 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)")
+ )
+ source_group = parser.add_mutually_exclusive_group()
+ source_group.add_argument(
+ "--src-ip",
+ metavar="<ip-address>",
+ help=_("Source IP address block (may use CIDR notation; "
+ "default for IPv4 rule: 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 update_parser_network(self, parser):
+ direction_group = parser.add_mutually_exclusive_group()
+ direction_group.add_argument(
+ '--ingress',
+ action='store_true',
+ help=_("Rule applies to incoming network traffic (default)")
+ )
+ direction_group.add_argument(
+ '--egress',
+ action='store_true',
+ help=_("Rule applies to outgoing network traffic")
+ )
+ parser.add_argument(
+ '--ethertype',
+ metavar='<ethertype>',
+ choices=['IPv4', 'IPv6'],
+ help=_("Ethertype of network traffic (IPv4, IPv6; default: IPv4)")
+ )
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("Owner's project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+ 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 = {}
+ # 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'
+ if parsed_args.ethertype:
+ attrs['ethertype'] = parsed_args.ethertype
+ 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':
+ 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
+ elif parsed_args.src_ip is not None:
+ attrs['remote_ip_prefix'] = parsed_args.src_ip
+ elif attrs['ethertype'] == 'IPv4':
+ attrs['remote_ip_prefix'] = '0.0.0.0/0'
+ attrs['security_group_id'] = security_group_id
+ if parsed_args.project is not None:
+ identity_client = self.app.client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_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
+ src_ip = None
+ if parsed_args.src_group is not None:
+ parsed_args.src_group = utils.find_resource(
+ client.security_groups,
+ parsed_args.src_group,
+ ).id
+ if parsed_args.src_ip is not None:
+ src_ip = parsed_args.src_ip
+ else:
+ src_ip = '0.0.0.0/0'
+ obj = client.security_group_rules.create(
+ group.id,
+ parsed_args.proto,
+ from_port,
+ to_port,
+ src_ip,
+ parsed_args.src_group,
+ )
+ return _format_security_group_rule_show(obj._info)
+
+
class DeleteSecurityGroupRule(common.NetworkAndComputeCommand):
"""Delete a security group rule"""
@@ -41,7 +219,7 @@ class DeleteSecurityGroupRule(common.NetworkAndComputeCommand):
parser.add_argument(
'rule',
metavar='<rule>',
- help='Security group rule to delete (ID only)',
+ help=_("Security group rule to delete (ID only)")
)
return parser
@@ -53,6 +231,141 @@ class DeleteSecurityGroupRule(common.NetworkAndComputeCommand):
client.security_group_rules.delete(parsed_args.rule)
+class ListSecurityGroupRule(common.NetworkAndComputeLister):
+ """List security group rules"""
+
+ def update_parser_common(self, parser):
+ parser.add_argument(
+ 'group',
+ metavar='<group>',
+ nargs='?',
+ help=_("List all rules in this security group (name or ID)")
+ )
+ return parser
+
+ def update_parser_network(self, parser):
+ # Accept but hide the argument for consistency with compute.
+ # Network will always return all projects for an admin.
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_("List additional fields in output")
+ )
+ return parser
+
+ def update_parser_compute(self, parser):
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=_("Display information from all projects (admin only)")
+ )
+ # Accept but hide the argument for consistency with network.
+ # There are no additional fields to display at this time.
+ parser.add_argument(
+ '--long',
+ action='store_false',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+ return parser
+
+ def _get_column_headers(self, parsed_args):
+ column_headers = (
+ 'ID',
+ 'IP Protocol',
+ 'IP Range',
+ 'Port Range',
+ )
+ if parsed_args.long:
+ column_headers = column_headers + ('Direction', 'Ethertype',)
+ column_headers = column_headers + ('Remote Security Group',)
+ if parsed_args.group is None:
+ column_headers = column_headers + ('Security Group',)
+ return column_headers
+
+ def take_action_network(self, client, parsed_args):
+ column_headers = self._get_column_headers(parsed_args)
+ columns = (
+ 'id',
+ 'protocol',
+ 'remote_ip_prefix',
+ 'port_range_min',
+ )
+ if parsed_args.long:
+ columns = columns + ('direction', 'ethertype',)
+ columns = columns + ('remote_group_id',)
+
+ # Get the security group rules using the requested query.
+ query = {}
+ if parsed_args.group is not None:
+ # NOTE(rtheis): Unfortunately, the security group resource
+ # does not contain security group rules resources. So use
+ # the security group ID in a query to get the resources.
+ security_group_id = client.find_security_group(
+ parsed_args.group,
+ ignore_missing=False
+ ).id
+ query = {'security_group_id': security_group_id}
+ else:
+ columns = columns + ('security_group_id',)
+ rules = list(client.security_group_rules(**query))
+
+ # Reformat the rules to display a port range instead
+ # of just the port range minimum. This maintains
+ # output compatibility with compute.
+ for rule in rules:
+ rule.port_range_min = _format_network_port_range(rule)
+
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in rules))
+
+ def take_action_compute(self, client, parsed_args):
+ column_headers = self._get_column_headers(parsed_args)
+ columns = (
+ "ID",
+ "IP Protocol",
+ "IP Range",
+ "Port Range",
+ "Remote Security Group",
+ )
+
+ rules_to_list = []
+ if parsed_args.group is not None:
+ group = utils.find_resource(
+ client.security_groups,
+ parsed_args.group,
+ )
+ rules_to_list = group.rules
+ else:
+ columns = columns + ('parent_group_id',)
+ search = {'all_tenants': parsed_args.all_projects}
+ for group in client.security_groups.list(search_opts=search):
+ rules_to_list.extend(group.rules)
+
+ # NOTE(rtheis): Turn the raw rules into resources.
+ rules = []
+ for rule in rules_to_list:
+ rules.append(compute_secgroup_rules.SecurityGroupRule(
+ client.security_group_rules,
+ network_utils.transform_compute_security_group_rule(rule),
+ ))
+
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in rules))
+
+
class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
"""Display security group rule details"""
@@ -60,7 +373,7 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
parser.add_argument(
'rule',
metavar="<rule>",
- help="Security group rule to display (ID only)"
+ help=_("Security group rule to display (ID only)")
)
return parser
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index 794c787f..715e6620 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -17,8 +17,10 @@ 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.i18n import _
from openstackclient.identity import common as identity_common
@@ -42,6 +44,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 "
+ "(repeat option to add multiple IP addresses)")
+ )
+ parser.add_argument(
+ '--dns-nameserver',
+ metavar='<dns-nameserver>',
+ action='append',
+ dest='dns_nameservers',
+ help=_("DNS server for this subnet "
+ "(repeat option to set multiple DNS servers)")
+ )
+ 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 "
+ "(repeat option to add multiple routes)")
+ )
+
+
def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
@@ -61,7 +96,7 @@ def convert_entries_to_nexthop(entries):
def convert_entries_to_gateway(entries):
- # Change 'nexhop' entry to 'gateway'
+ # Change 'nexthop' entry to 'gateway'
changed_entries = copy.deepcopy(entries)
for entry in changed_entries:
entry['gateway'] = entry['nexthop']
@@ -70,57 +105,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
@@ -131,122 +175,89 @@ class CreateSubnet(command.ShowOne):
parser = super(CreateSubnet, self).get_parser(prog_name)
parser.add_argument(
'name',
- help='New subnet name',
+ help=_("New subnet name")
)
parser.add_argument(
'--project',
metavar='<project>',
- help="Owner's project (name or ID)",
+ help=_("Owner's project (name or ID)")
)
identity_common.add_project_domain_option_to_parser(parser)
subnet_pool_group = parser.add_mutually_exclusive_group()
subnet_pool_group.add_argument(
'--subnet-pool',
metavar='<subnet-pool>',
- help='Subnet pool from which this subnet will obtain a CIDR '
- '(Name or ID)',
+ help=_("Subnet pool from which this subnet will obtain a CIDR "
+ "(Name or ID)")
)
subnet_pool_group.add_argument(
'--use-default-subnet-pool',
action='store_true',
- help='Use default subnet pool for --ip-version',
+ help=_("Use default subnet pool for --ip-version")
)
parser.add_argument(
'--prefix-length',
metavar='<prefix-length>',
- help='Prefix length for subnet allocation from subnetpool',
+ help=_("Prefix length for subnet allocation from subnet pool")
)
parser.add_argument(
'--subnet-range',
metavar='<subnet-range>',
- help='Subnet range in CIDR notation '
- '(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)',
+ help=_("Subnet range in CIDR notation "
+ "(required if --subnet-pool is not specified, "
+ "optional otherwise)")
)
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)',
+ help=_("Enable DHCP (default)")
)
dhcp_enable_group.add_argument(
'--no-dhcp',
- dest='enable_dhcp',
- action='store_false',
- 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)',
+ action='store_true',
+ help=_("Disable DHCP")
)
parser.add_argument(
'--gateway',
metavar='<gateway>',
default='auto',
- help="Specify a gateway for the subnet. The three options are: "
- " <ip-address>: Specific IP address to use as the gateway "
- " 'auto': Gateway address should automatically be "
- " chosen from within the subnet itself "
- " 'none': This subnet will not use a gateway "
- "e.g.: --gateway 192.168.9.1, --gateway auto, --gateway none"
- "(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)',
+ help=_("Specify a gateway for the subnet. The three options are: "
+ "<ip-address>: Specific IP address to use as the gateway, "
+ "'auto': Gateway address should automatically be chosen "
+ "from within the subnet itself, 'none': This subnet will "
+ "not use a gateway, e.g.: --gateway 192.168.9.1, "
+ "--gateway auto, --gateway none (default is 'auto')")
)
parser.add_argument(
'--ip-version',
type=int,
default=4,
choices=[4, 6],
- help='IP version (default is 4). Note that when subnet pool is '
- 'specified, IP version is determined from the subnet pool '
- 'and this option is ignored.',
+ help=_("IP version (default is 4). Note that when subnet pool is "
+ "specified, IP version is determined from the subnet pool "
+ "and this option is ignored")
)
parser.add_argument(
'--ipv6-ra-mode',
choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
- help='IPv6 RA (Router Advertisement) mode, '
- 'valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]',
+ help=_("IPv6 RA (Router Advertisement) mode, "
+ "valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]")
)
parser.add_argument(
'--ipv6-address-mode',
choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
- help='IPv6 address mode, '
- 'valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]',
+ help=_("IPv6 address mode, "
+ "valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]")
)
parser.add_argument(
'--network',
required=True,
metavar='<network>',
- help='Network this subnet belongs to (name or ID)',
+ help=_("Network this subnet belongs to (name or ID)")
)
-
+ _get_common_parse_arguments(parser)
return parser
def take_action(self, parsed_args):
@@ -266,7 +277,7 @@ class DeleteSubnet(command.Command):
parser.add_argument(
'subnet',
metavar="<subnet>",
- help="Subnet to delete (name or ID)",
+ help=_("Subnet to delete (name or ID)")
)
return parser
@@ -285,7 +296,7 @@ class ListSubnet(command.Lister):
'--long',
action='store_true',
default=False,
- help='List additional fields in output',
+ help=_("List additional fields in output")
)
return parser
@@ -309,15 +320,71 @@ 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)
+ if 'dns_nameservers' in attrs:
+ attrs['dns_nameservers'] += obj.dns_nameservers
+ if 'host_routes' in attrs:
+ attrs['host_routes'] += obj.host_routes
+ if 'allocation_pools' in attrs:
+ attrs['allocation_pools'] += obj.allocation_pools
+ client.update_subnet(obj, **attrs)
+ return
+
+
class ShowSubnet(command.ShowOne):
- """Show subnet details"""
+ """Display subnet details"""
def get_parser(self, prog_name):
parser = super(ShowSubnet, self).get_parser(prog_name)
parser.add_argument(
'subnet',
metavar="<subnet>",
- help="Subnet to show (name or ID)",
+ help=_("Subnet to display (name or ID)")
)
return parser
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index d0d8d058..482b5ecf 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -17,6 +17,8 @@ from openstackclient.common import command
from openstackclient.common import exceptions
from openstackclient.common import parseractions
from openstackclient.common import utils
+from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
def _get_columns(item):
@@ -32,18 +34,36 @@ _formatters = {
}
-def _get_attrs(parsed_args):
+def _get_attrs(client_manager, parsed_args):
attrs = {}
+ network_client = client_manager.network
+
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
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
+
+ if parsed_args.address_scope is not None:
+ attrs['address_scope_id'] = network_client.find_address_scope(
+ parsed_args.address_scope, ignore_missing=False).id
+ if 'no_address_scope' in parsed_args and parsed_args.no_address_scope:
+ attrs['address_scope_id'] = None
+
+ # "subnet pool set" command doesn't support setting project.
+ 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
return attrs
@@ -54,26 +74,26 @@ def _add_prefix_options(parser):
metavar='<pool-prefix>',
dest='prefixes',
action='append',
- help='Set subnet pool prefixes (in CIDR notation). '
- 'Repeat this option to set multiple prefixes.',
+ help=_("Set subnet pool prefixes (in CIDR notation) "
+ "(repeat option to set multiple prefixes)")
)
parser.add_argument(
'--default-prefix-length',
metavar='<default-prefix-length>',
action=parseractions.NonNegativeAction,
- help='Set subnet pool default prefix length',
+ help=_("Set subnet pool default prefix length")
)
parser.add_argument(
'--min-prefix-length',
metavar='<min-prefix-length>',
action=parseractions.NonNegativeAction,
- help='Set subnet pool minimum prefix length',
+ help=_("Set subnet pool minimum prefix length")
)
parser.add_argument(
'--max-prefix-length',
metavar='<max-prefix-length>',
action=parseractions.NonNegativeAction,
- help='Set subnet pool maximum prefix length',
+ help=_("Set subnet pool maximum prefix length")
)
@@ -84,16 +104,31 @@ class CreateSubnetPool(command.ShowOne):
parser = super(CreateSubnetPool, self).get_parser(prog_name)
parser.add_argument(
'name',
- metavar="<name>",
- help='Name of the new subnet pool'
+ metavar='<name>',
+ help=_("Name of the new subnet pool")
)
_add_prefix_options(parser)
-
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("Owner's project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+ parser.add_argument(
+ '--address-scope',
+ metavar='<address-scope>',
+ help=_("Set address scope associated with the subnet pool "
+ "(name or ID), prefixes must be unique across address "
+ "scopes")
+ )
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
- attrs = _get_attrs(parsed_args)
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+ # NeutronServer expects prefixes to be a List
+ if "prefixes" not in attrs:
+ attrs['prefixes'] = []
obj = client.create_subnet_pool(**attrs)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
@@ -108,7 +143,7 @@ class DeleteSubnetPool(command.Command):
parser.add_argument(
'subnet_pool',
metavar='<subnet-pool>',
- help='Subnet pool to delete (name or ID)'
+ help=_("Subnet pool to delete (name or ID)")
)
return parser
@@ -127,7 +162,7 @@ class ListSubnetPool(command.Lister):
'--long',
action='store_true',
default=False,
- help='List additional fields in output',
+ help=_("List additional fields in output")
)
return parser
@@ -164,7 +199,7 @@ class ListSubnetPool(command.Lister):
return (headers,
(utils.get_item_properties(
s, columns,
- formatters={},
+ formatters=_formatters,
) for s in data))
@@ -176,15 +211,27 @@ class SetSubnetPool(command.Command):
parser.add_argument(
'subnet_pool',
metavar='<subnet-pool>',
- help='Subnet pool to modify (name or ID)'
+ help=_("Subnet pool to modify (name or ID)")
)
parser.add_argument(
'--name',
metavar='<name>',
- help='Set subnet pool name',
+ help=_("Set subnet pool name")
)
_add_prefix_options(parser)
-
+ address_scope_group = parser.add_mutually_exclusive_group()
+ address_scope_group.add_argument(
+ '--address-scope',
+ metavar='<address-scope>',
+ help=_("Set address scope associated with the subnet pool "
+ "(name or ID), prefixes must be unique across address "
+ "scopes")
+ )
+ address_scope_group.add_argument(
+ '--no-address-scope',
+ action='store_true',
+ help=_("Remove address scope associated with the subnet pool")
+ )
return parser
def take_action(self, parsed_args):
@@ -192,7 +239,7 @@ class SetSubnetPool(command.Command):
obj = client.find_subnet_pool(parsed_args.subnet_pool,
ignore_missing=False)
- attrs = _get_attrs(parsed_args)
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
if attrs == {}:
msg = "Nothing specified to be set"
raise exceptions.CommandError(msg)
@@ -212,7 +259,7 @@ class ShowSubnetPool(command.ShowOne):
parser.add_argument(
'subnet_pool',
metavar='<subnet-pool>',
- help='Subnet pool to display (name or ID)'
+ help=_("Subnet pool to display (name or ID)")
)
return parser