Merge "Add "os subnet create" command using SDK"
This commit is contained in:
		@@ -20,6 +20,114 @@ Delete a subnet
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Subnet to delete (name or ID)
 | 
					    Subnet to delete (name or ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					subnet create
 | 
				
			||||||
 | 
					--------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Create new subnet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. program:: subnet create
 | 
				
			||||||
 | 
					.. code:: bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    os subnet create
 | 
				
			||||||
 | 
					        [--project <project> [--project-domain <project-domain>]]
 | 
				
			||||||
 | 
					        [--subnet-pool <subnet-pool> | --use-default-subnet-pool [--prefix-length <prefix-length>]]
 | 
				
			||||||
 | 
					        [--subnet-range <subnet-range>]
 | 
				
			||||||
 | 
					        [--allocation-pool start=<ip-address>,end=<ip-address>]
 | 
				
			||||||
 | 
					        [--dhcp | --no-dhcp]
 | 
				
			||||||
 | 
					        [--dns-nameserver <dns-nameserver>]
 | 
				
			||||||
 | 
					        [--gateway <gateway>]
 | 
				
			||||||
 | 
					        [--host-route destination=<subnet>,gateway=<ip-address>]
 | 
				
			||||||
 | 
					        [--ip-version {4,6}]
 | 
				
			||||||
 | 
					        [--ipv6-ra-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}]
 | 
				
			||||||
 | 
					        [--ipv6-address-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}]
 | 
				
			||||||
 | 
					        --network <network>
 | 
				
			||||||
 | 
					        <name>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --project <project>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Owner's project (name or ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --project-domain <project-domain>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Domain the project belongs to (name or ID).
 | 
				
			||||||
 | 
					    This can be used in case collisions between project names exist.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --subnet-pool <subnet-pool>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Subnet pool from which this subnet will obtain a CIDR (name or ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --use-default-subnet-pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Use default subnet pool for --ip-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --prefix-length <prefix-length>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Prefix length for subnet allocation from subnet pool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --subnet-range <subnet-range>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Subnet range in CIDR notation
 | 
				
			||||||
 | 
					        (required if --subnet-pool is not specified, optional otherwise)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --allocation-pool start=<ip-address>,end=<ip-address>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Allocation pool IP addresses for this subnet e.g.:
 | 
				
			||||||
 | 
					        start=192.168.199.2,end=192.168.199.254 (This option can be repeated)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --dhcp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Enable DHCP (default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --no-dhcp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Disable DHCP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --dns-nameserver <dns-nameserver>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     DNS name server for this subnet (This option can be repeated)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --gateway <gateway>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     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')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --host-route destination=<subnet>,gateway=<ip-address>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --ip-version {4,6}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --ipv6-ra-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     IPv6 RA (Router Advertisement) mode,
 | 
				
			||||||
 | 
					         valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --ipv6-address-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     IPv6 address mode, valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. option:: --network <network>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Network this subnet belongs to (name or ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _subnet_create-name:
 | 
				
			||||||
 | 
					.. describe:: <name>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Name of subnet to create
 | 
				
			||||||
 | 
					
 | 
				
			||||||
subnet list
 | 
					subnet list
 | 
				
			||||||
-----------
 | 
					-----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,9 +12,14 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""Subnet action implementations"""
 | 
					"""Subnet action implementations"""
 | 
				
			||||||
 | 
					import copy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from json.encoder import JSONEncoder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from openstackclient.common import command
 | 
					from openstackclient.common import command
 | 
				
			||||||
 | 
					from openstackclient.common import parseractions
 | 
				
			||||||
from openstackclient.common import utils
 | 
					from openstackclient.common import utils
 | 
				
			||||||
 | 
					from openstackclient.identity import common as identity_common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _format_allocation_pools(data):
 | 
					def _format_allocation_pools(data):
 | 
				
			||||||
@@ -23,10 +28,17 @@ def _format_allocation_pools(data):
 | 
				
			|||||||
    return ','.join(pool_formatted)
 | 
					    return ','.join(pool_formatted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _format_host_routes(data):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        return '\n'.join([JSONEncoder().encode(route) for route in data])
 | 
				
			||||||
 | 
					    except (TypeError, KeyError):
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_formatters = {
 | 
					_formatters = {
 | 
				
			||||||
    'allocation_pools': _format_allocation_pools,
 | 
					    'allocation_pools': _format_allocation_pools,
 | 
				
			||||||
    'dns_nameservers': utils.format_list,
 | 
					    'dns_nameservers': utils.format_list,
 | 
				
			||||||
    'host_routes': utils.format_list,
 | 
					    'host_routes': _format_host_routes,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,6 +50,214 @@ def _get_columns(item):
 | 
				
			|||||||
    return tuple(sorted(columns))
 | 
					    return tuple(sorted(columns))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def convert_entries_to_nexthop(entries):
 | 
				
			||||||
 | 
					    # Change 'gateway' entry to 'nexthop'
 | 
				
			||||||
 | 
					    changed_entries = copy.deepcopy(entries)
 | 
				
			||||||
 | 
					    for entry in changed_entries:
 | 
				
			||||||
 | 
					        entry['nexthop'] = entry['gateway']
 | 
				
			||||||
 | 
					        del entry['gateway']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return changed_entries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def convert_entries_to_gateway(entries):
 | 
				
			||||||
 | 
					    # Change 'nexhop' entry to 'gateway'
 | 
				
			||||||
 | 
					    changed_entries = copy.deepcopy(entries)
 | 
				
			||||||
 | 
					    for entry in changed_entries:
 | 
				
			||||||
 | 
					        entry['gateway'] = entry['nexthop']
 | 
				
			||||||
 | 
					        del entry['nexthop']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return changed_entries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_attrs(client_manager, parsed_args):
 | 
				
			||||||
 | 
					    attrs = {}
 | 
				
			||||||
 | 
					    if 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:
 | 
				
			||||||
 | 
					        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:
 | 
				
			||||||
 | 
					        attrs['dns_nameservers'] = parsed_args.dns_nameservers
 | 
				
			||||||
 | 
					    if 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CreateSubnet(command.ShowOne):
 | 
				
			||||||
 | 
					    """Create a subnet"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_parser(self, prog_name):
 | 
				
			||||||
 | 
					        parser = super(CreateSubnet, self).get_parser(prog_name)
 | 
				
			||||||
 | 
					        parser.add_argument(
 | 
				
			||||||
 | 
					            'name',
 | 
				
			||||||
 | 
					            help='New subnet name',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        parser.add_argument(
 | 
				
			||||||
 | 
					            '--project',
 | 
				
			||||||
 | 
					            metavar='<project>',
 | 
				
			||||||
 | 
					            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)',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        subnet_pool_group.add_argument(
 | 
				
			||||||
 | 
					            '--use-default-subnet-pool',
 | 
				
			||||||
 | 
					            action='store_true',
 | 
				
			||||||
 | 
					            help='Use default subnet pool for --ip-version',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        parser.add_argument(
 | 
				
			||||||
 | 
					            '--prefix-length',
 | 
				
			||||||
 | 
					            metavar='<prefix-length>',
 | 
				
			||||||
 | 
					            help='Prefix length for subnet allocation from subnetpool',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        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)',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        dhcp_enable_group = parser.add_mutually_exclusive_group()
 | 
				
			||||||
 | 
					        dhcp_enable_group.add_argument(
 | 
				
			||||||
 | 
					            '--dhcp',
 | 
				
			||||||
 | 
					            dest='enable_dhcp',
 | 
				
			||||||
 | 
					            action='store_true',
 | 
				
			||||||
 | 
					            default=True,
 | 
				
			||||||
 | 
					            help='Enable DHCP (default)',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        dhcp_enable_group.add_argument(
 | 
				
			||||||
 | 
					            '--no-dhcp',
 | 
				
			||||||
 | 
					            dest='enable_dhcp',
 | 
				
			||||||
 | 
					            action='store_false',
 | 
				
			||||||
 | 
					            help='Disable DHCP',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        parser.add_argument(
 | 
				
			||||||
 | 
					            '--dns-nameserver',
 | 
				
			||||||
 | 
					            metavar='<dns-nameserver>',
 | 
				
			||||||
 | 
					            action='append',
 | 
				
			||||||
 | 
					            dest='dns_nameservers',
 | 
				
			||||||
 | 
					            help='DNS name server for this subnet '
 | 
				
			||||||
 | 
					                 '(This option can be repeated)',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        parser.add_argument(
 | 
				
			||||||
 | 
					            '--gateway',
 | 
				
			||||||
 | 
					            metavar='<gateway>',
 | 
				
			||||||
 | 
					            default='auto',
 | 
				
			||||||
 | 
					            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)',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        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.',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        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]',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        parser.add_argument(
 | 
				
			||||||
 | 
					            '--ipv6-address-mode',
 | 
				
			||||||
 | 
					            choices=['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)',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def take_action(self, parsed_args):
 | 
				
			||||||
 | 
					        client = self.app.client_manager.network
 | 
				
			||||||
 | 
					        attrs = _get_attrs(self.app.client_manager, parsed_args)
 | 
				
			||||||
 | 
					        obj = client.create_subnet(**attrs)
 | 
				
			||||||
 | 
					        columns = _get_columns(obj)
 | 
				
			||||||
 | 
					        data = utils.get_item_properties(obj, columns, formatters=_formatters)
 | 
				
			||||||
 | 
					        return (columns, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeleteSubnet(command.Command):
 | 
					class DeleteSubnet(command.Command):
 | 
				
			||||||
    """Delete subnet"""
 | 
					    """Delete subnet"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,7 +266,7 @@ class DeleteSubnet(command.Command):
 | 
				
			|||||||
        parser.add_argument(
 | 
					        parser.add_argument(
 | 
				
			||||||
            'subnet',
 | 
					            'subnet',
 | 
				
			||||||
            metavar="<subnet>",
 | 
					            metavar="<subnet>",
 | 
				
			||||||
            help="Subnet to delete (name or ID)"
 | 
					            help="Subnet to delete (name or ID)",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return parser
 | 
					        return parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -97,7 +317,7 @@ class ShowSubnet(command.ShowOne):
 | 
				
			|||||||
        parser.add_argument(
 | 
					        parser.add_argument(
 | 
				
			||||||
            'subnet',
 | 
					            'subnet',
 | 
				
			||||||
            metavar="<subnet>",
 | 
					            metavar="<subnet>",
 | 
				
			||||||
            help="Subnet to show (name or ID)"
 | 
					            help="Subnet to show (name or ID)",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return parser
 | 
					        return parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -590,7 +590,7 @@ class FakeSubnet(object):
 | 
				
			|||||||
            'dns_nameservers': [],
 | 
					            'dns_nameservers': [],
 | 
				
			||||||
            'allocation_pools': [],
 | 
					            'allocation_pools': [],
 | 
				
			||||||
            'host_routes': [],
 | 
					            'host_routes': [],
 | 
				
			||||||
            'ip_version': '4',
 | 
					            'ip_version': 4,
 | 
				
			||||||
            'gateway_ip': '10.10.10.1',
 | 
					            'gateway_ip': '10.10.10.1',
 | 
				
			||||||
            'ipv6_address_mode': 'None',
 | 
					            'ipv6_address_mode': 'None',
 | 
				
			||||||
            'ipv6_ra_mode': 'None',
 | 
					            'ipv6_ra_mode': 'None',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,10 +11,13 @@
 | 
				
			|||||||
#   under the License.
 | 
					#   under the License.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import copy
 | 
				
			||||||
import mock
 | 
					import mock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from openstackclient.common import utils
 | 
					from openstackclient.common import utils
 | 
				
			||||||
from openstackclient.network.v2 import subnet as subnet_v2
 | 
					from openstackclient.network.v2 import subnet as subnet_v2
 | 
				
			||||||
 | 
					from openstackclient.tests import fakes
 | 
				
			||||||
 | 
					from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3
 | 
				
			||||||
from openstackclient.tests.network.v2 import fakes as network_fakes
 | 
					from openstackclient.tests.network.v2 import fakes as network_fakes
 | 
				
			||||||
from openstackclient.tests import utils as tests_utils
 | 
					from openstackclient.tests import utils as tests_utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,6 +31,333 @@ class TestSubnet(network_fakes.TestNetworkV2):
 | 
				
			|||||||
        self.network = self.app.client_manager.network
 | 
					        self.network = self.app.client_manager.network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestCreateSubnet(TestSubnet):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # An IPv4 subnet to be created with mostly default values
 | 
				
			||||||
 | 
					    _subnet = network_fakes.FakeSubnet.create_one_subnet(
 | 
				
			||||||
 | 
					        attrs={
 | 
				
			||||||
 | 
					            'tenant_id': identity_fakes_v3.project_id,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Subnet pool to be used to create a subnet from a pool
 | 
				
			||||||
 | 
					    _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # An IPv4 subnet to be created using a specific subnet pool
 | 
				
			||||||
 | 
					    _subnet_from_pool = network_fakes.FakeSubnet.create_one_subnet(
 | 
				
			||||||
 | 
					        attrs={
 | 
				
			||||||
 | 
					            'tenant_id': identity_fakes_v3.project_id,
 | 
				
			||||||
 | 
					            'subnetpool_id': _subnet_pool.id,
 | 
				
			||||||
 | 
					            'dns_nameservers': ['8.8.8.8',
 | 
				
			||||||
 | 
					                                '8.8.4.4'],
 | 
				
			||||||
 | 
					            'host_routes': [{'destination': '10.20.20.0/24',
 | 
				
			||||||
 | 
					                             'nexthop': '10.20.20.1'},
 | 
				
			||||||
 | 
					                            {'destination': '10.30.30.0/24',
 | 
				
			||||||
 | 
					                             'nexthop': '10.30.30.1'}],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # An IPv6 subnet to be created with most options specified
 | 
				
			||||||
 | 
					    _subnet_ipv6 = network_fakes.FakeSubnet.create_one_subnet(
 | 
				
			||||||
 | 
					        attrs={
 | 
				
			||||||
 | 
					            'tenant_id': identity_fakes_v3.project_id,
 | 
				
			||||||
 | 
					            'cidr': 'fe80:0:0:a00a::/64',
 | 
				
			||||||
 | 
					            'enable_dhcp': True,
 | 
				
			||||||
 | 
					            'dns_nameservers': ['fe80:27ff:a00a:f00f::ffff',
 | 
				
			||||||
 | 
					                                'fe80:37ff:a00a:f00f::ffff'],
 | 
				
			||||||
 | 
					            'allocation_pools': [{'start': 'fe80::a00a:0:c0de:0:100',
 | 
				
			||||||
 | 
					                                  'end': 'fe80::a00a:0:c0de:0:f000'},
 | 
				
			||||||
 | 
					                                 {'start': 'fe80::a00a:0:c0de:1:100',
 | 
				
			||||||
 | 
					                                  'end': 'fe80::a00a:0:c0de:1:f000'}],
 | 
				
			||||||
 | 
					            'host_routes': [{'destination': 'fe80:27ff:a00a:f00f::/64',
 | 
				
			||||||
 | 
					                             'nexthop': 'fe80:27ff:a00a:f00f::1'},
 | 
				
			||||||
 | 
					                            {'destination': 'fe80:37ff:a00a:f00f::/64',
 | 
				
			||||||
 | 
					                             'nexthop': 'fe80:37ff:a00a:f00f::1'}],
 | 
				
			||||||
 | 
					            'ip_version': 6,
 | 
				
			||||||
 | 
					            'gateway_ip': 'fe80::a00a:0:c0de:0:1',
 | 
				
			||||||
 | 
					            'ipv6_address_mode': 'slaac',
 | 
				
			||||||
 | 
					            'ipv6_ra_mode': 'slaac',
 | 
				
			||||||
 | 
					            'subnetpool_id': 'None',
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # The network to be returned from find_network
 | 
				
			||||||
 | 
					    _network = network_fakes.FakeNetwork.create_one_network(
 | 
				
			||||||
 | 
					        attrs={
 | 
				
			||||||
 | 
					            'id': _subnet.network_id,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    columns = (
 | 
				
			||||||
 | 
					        'allocation_pools',
 | 
				
			||||||
 | 
					        'cidr',
 | 
				
			||||||
 | 
					        'dns_nameservers',
 | 
				
			||||||
 | 
					        'enable_dhcp',
 | 
				
			||||||
 | 
					        'gateway_ip',
 | 
				
			||||||
 | 
					        'host_routes',
 | 
				
			||||||
 | 
					        'id',
 | 
				
			||||||
 | 
					        'ip_version',
 | 
				
			||||||
 | 
					        'ipv6_address_mode',
 | 
				
			||||||
 | 
					        'ipv6_ra_mode',
 | 
				
			||||||
 | 
					        'name',
 | 
				
			||||||
 | 
					        'network_id',
 | 
				
			||||||
 | 
					        'project_id',
 | 
				
			||||||
 | 
					        'subnetpool_id',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data = (
 | 
				
			||||||
 | 
					        subnet_v2._format_allocation_pools(_subnet.allocation_pools),
 | 
				
			||||||
 | 
					        _subnet.cidr,
 | 
				
			||||||
 | 
					        utils.format_list(_subnet.dns_nameservers),
 | 
				
			||||||
 | 
					        _subnet.enable_dhcp,
 | 
				
			||||||
 | 
					        _subnet.gateway_ip,
 | 
				
			||||||
 | 
					        subnet_v2._format_host_routes(_subnet.host_routes),
 | 
				
			||||||
 | 
					        _subnet.id,
 | 
				
			||||||
 | 
					        _subnet.ip_version,
 | 
				
			||||||
 | 
					        _subnet.ipv6_address_mode,
 | 
				
			||||||
 | 
					        _subnet.ipv6_ra_mode,
 | 
				
			||||||
 | 
					        _subnet.name,
 | 
				
			||||||
 | 
					        _subnet.network_id,
 | 
				
			||||||
 | 
					        _subnet.project_id,
 | 
				
			||||||
 | 
					        _subnet.subnetpool_id,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data_subnet_pool = (
 | 
				
			||||||
 | 
					        subnet_v2._format_allocation_pools(_subnet_from_pool.allocation_pools),
 | 
				
			||||||
 | 
					        _subnet_from_pool.cidr,
 | 
				
			||||||
 | 
					        utils.format_list(_subnet_from_pool.dns_nameservers),
 | 
				
			||||||
 | 
					        _subnet_from_pool.enable_dhcp,
 | 
				
			||||||
 | 
					        _subnet_from_pool.gateway_ip,
 | 
				
			||||||
 | 
					        subnet_v2._format_host_routes(_subnet_from_pool.host_routes),
 | 
				
			||||||
 | 
					        _subnet_from_pool.id,
 | 
				
			||||||
 | 
					        _subnet_from_pool.ip_version,
 | 
				
			||||||
 | 
					        _subnet_from_pool.ipv6_address_mode,
 | 
				
			||||||
 | 
					        _subnet_from_pool.ipv6_ra_mode,
 | 
				
			||||||
 | 
					        _subnet_from_pool.name,
 | 
				
			||||||
 | 
					        _subnet_from_pool.network_id,
 | 
				
			||||||
 | 
					        _subnet_from_pool.project_id,
 | 
				
			||||||
 | 
					        _subnet_from_pool.subnetpool_id,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data_ipv6 = (
 | 
				
			||||||
 | 
					        subnet_v2._format_allocation_pools(_subnet_ipv6.allocation_pools),
 | 
				
			||||||
 | 
					        _subnet_ipv6.cidr,
 | 
				
			||||||
 | 
					        utils.format_list(_subnet_ipv6.dns_nameservers),
 | 
				
			||||||
 | 
					        _subnet_ipv6.enable_dhcp,
 | 
				
			||||||
 | 
					        _subnet_ipv6.gateway_ip,
 | 
				
			||||||
 | 
					        subnet_v2._format_host_routes(_subnet_ipv6.host_routes),
 | 
				
			||||||
 | 
					        _subnet_ipv6.id,
 | 
				
			||||||
 | 
					        _subnet_ipv6.ip_version,
 | 
				
			||||||
 | 
					        _subnet_ipv6.ipv6_address_mode,
 | 
				
			||||||
 | 
					        _subnet_ipv6.ipv6_ra_mode,
 | 
				
			||||||
 | 
					        _subnet_ipv6.name,
 | 
				
			||||||
 | 
					        _subnet_ipv6.network_id,
 | 
				
			||||||
 | 
					        _subnet_ipv6.project_id,
 | 
				
			||||||
 | 
					        _subnet_ipv6.subnetpool_id,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        super(TestCreateSubnet, self).setUp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Get the command object to test
 | 
				
			||||||
 | 
					        self.cmd = subnet_v2.CreateSubnet(self.app, self.namespace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Set identity client v3. And get a shortcut to Identity client.
 | 
				
			||||||
 | 
					        identity_client = identity_fakes_v3.FakeIdentityv3Client(
 | 
				
			||||||
 | 
					            endpoint=fakes.AUTH_URL,
 | 
				
			||||||
 | 
					            token=fakes.AUTH_TOKEN,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.app.client_manager.identity = identity_client
 | 
				
			||||||
 | 
					        self.identity = self.app.client_manager.identity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Get a shortcut to the ProjectManager Mock
 | 
				
			||||||
 | 
					        self.projects_mock = self.identity.projects
 | 
				
			||||||
 | 
					        self.projects_mock.get.return_value = fakes.FakeResource(
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            copy.deepcopy(identity_fakes_v3.PROJECT),
 | 
				
			||||||
 | 
					            loaded=True,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Get a shortcut to the DomainManager Mock
 | 
				
			||||||
 | 
					        self.domains_mock = self.identity.domains
 | 
				
			||||||
 | 
					        self.domains_mock.get.return_value = fakes.FakeResource(
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            copy.deepcopy(identity_fakes_v3.DOMAIN),
 | 
				
			||||||
 | 
					            loaded=True,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_create_no_options(self):
 | 
				
			||||||
 | 
					        arglist = []
 | 
				
			||||||
 | 
					        verifylist = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Testing that a call without the required argument will fail and
 | 
				
			||||||
 | 
					        # throw a "ParserExecption"
 | 
				
			||||||
 | 
					        self.assertRaises(tests_utils.ParserException,
 | 
				
			||||||
 | 
					                          self.check_parser, self.cmd, arglist, verifylist)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_create_default_options(self):
 | 
				
			||||||
 | 
					        # Mock create_subnet and find_network sdk calls to return the
 | 
				
			||||||
 | 
					        # values we want for this test
 | 
				
			||||||
 | 
					        self.network.create_subnet = mock.Mock(return_value=self._subnet)
 | 
				
			||||||
 | 
					        self._network.id = self._subnet.network_id
 | 
				
			||||||
 | 
					        self.network.find_network = mock.Mock(return_value=self._network)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        arglist = [
 | 
				
			||||||
 | 
					            self._subnet.name,
 | 
				
			||||||
 | 
					            "--subnet-range", self._subnet.cidr,
 | 
				
			||||||
 | 
					            "--network", self._subnet.network_id,
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        verifylist = [
 | 
				
			||||||
 | 
					            ('name', self._subnet.name),
 | 
				
			||||||
 | 
					            ('subnet_range', self._subnet.cidr),
 | 
				
			||||||
 | 
					            ('network', self._subnet.network_id),
 | 
				
			||||||
 | 
					            ('ip_version', self._subnet.ip_version),
 | 
				
			||||||
 | 
					            ('gateway', 'auto'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 | 
				
			||||||
 | 
					        columns, data = self.cmd.take_action(parsed_args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.network.create_subnet.assert_called_with(**{
 | 
				
			||||||
 | 
					            'cidr': self._subnet.cidr,
 | 
				
			||||||
 | 
					            'enable_dhcp': self._subnet.enable_dhcp,
 | 
				
			||||||
 | 
					            'ip_version': self._subnet.ip_version,
 | 
				
			||||||
 | 
					            'name': self._subnet.name,
 | 
				
			||||||
 | 
					            'network_id': self._subnet.network_id,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        self.assertEqual(self.columns, columns)
 | 
				
			||||||
 | 
					        self.assertEqual(self.data, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_create_from_subnet_pool_options(self):
 | 
				
			||||||
 | 
					        # Mock create_subnet, find_subnet_pool, and find_network sdk calls
 | 
				
			||||||
 | 
					        # to return the values we want for this test
 | 
				
			||||||
 | 
					        self.network.create_subnet = \
 | 
				
			||||||
 | 
					            mock.Mock(return_value=self._subnet_from_pool)
 | 
				
			||||||
 | 
					        self._network.id = self._subnet_from_pool.network_id
 | 
				
			||||||
 | 
					        self.network.find_network = mock.Mock(return_value=self._network)
 | 
				
			||||||
 | 
					        self.network.find_subnet_pool = \
 | 
				
			||||||
 | 
					            mock.Mock(return_value=self._subnet_pool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        arglist = [
 | 
				
			||||||
 | 
					            self._subnet_from_pool.name,
 | 
				
			||||||
 | 
					            "--subnet-pool", self._subnet_from_pool.subnetpool_id,
 | 
				
			||||||
 | 
					            "--prefix-length", '24',
 | 
				
			||||||
 | 
					            "--network", self._subnet_from_pool.network_id,
 | 
				
			||||||
 | 
					            "--ip-version", str(self._subnet_from_pool.ip_version),
 | 
				
			||||||
 | 
					            "--gateway", self._subnet_from_pool.gateway_ip,
 | 
				
			||||||
 | 
					            "--dhcp",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for dns_addr in self._subnet_from_pool.dns_nameservers:
 | 
				
			||||||
 | 
					            arglist.append('--dns-nameserver')
 | 
				
			||||||
 | 
					            arglist.append(dns_addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for host_route in self._subnet_from_pool.host_routes:
 | 
				
			||||||
 | 
					            arglist.append('--host-route')
 | 
				
			||||||
 | 
					            value = 'gateway=' + host_route.get('nexthop', '') + \
 | 
				
			||||||
 | 
					                    ',destination=' + host_route.get('destination', '')
 | 
				
			||||||
 | 
					            arglist.append(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        verifylist = [
 | 
				
			||||||
 | 
					            ('name', self._subnet_from_pool.name),
 | 
				
			||||||
 | 
					            ('prefix_length', '24'),
 | 
				
			||||||
 | 
					            ('network', self._subnet_from_pool.network_id),
 | 
				
			||||||
 | 
					            ('ip_version', self._subnet_from_pool.ip_version),
 | 
				
			||||||
 | 
					            ('gateway', self._subnet_from_pool.gateway_ip),
 | 
				
			||||||
 | 
					            ('dns_nameservers', self._subnet_from_pool.dns_nameservers),
 | 
				
			||||||
 | 
					            ('enable_dhcp', self._subnet_from_pool.enable_dhcp),
 | 
				
			||||||
 | 
					            ('host_routes', subnet_v2.convert_entries_to_gateway(
 | 
				
			||||||
 | 
					                self._subnet_from_pool.host_routes)),
 | 
				
			||||||
 | 
					            ('subnet_pool', self._subnet_from_pool.subnetpool_id),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 | 
				
			||||||
 | 
					        columns, data = self.cmd.take_action(parsed_args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.network.create_subnet.assert_called_with(**{
 | 
				
			||||||
 | 
					            'dns_nameservers': self._subnet_from_pool.dns_nameservers,
 | 
				
			||||||
 | 
					            'enable_dhcp': self._subnet_from_pool.enable_dhcp,
 | 
				
			||||||
 | 
					            'gateway_ip': self._subnet_from_pool.gateway_ip,
 | 
				
			||||||
 | 
					            'host_routes': self._subnet_from_pool.host_routes,
 | 
				
			||||||
 | 
					            'ip_version': self._subnet_from_pool.ip_version,
 | 
				
			||||||
 | 
					            'name': self._subnet_from_pool.name,
 | 
				
			||||||
 | 
					            'network_id': self._subnet_from_pool.network_id,
 | 
				
			||||||
 | 
					            'prefixlen': '24',
 | 
				
			||||||
 | 
					            'subnetpool_id': self._subnet_from_pool.subnetpool_id,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        self.assertEqual(self.columns, columns)
 | 
				
			||||||
 | 
					        self.assertEqual(self.data_subnet_pool, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_create_options_subnet_range_ipv6(self):
 | 
				
			||||||
 | 
					        # Mock create_subnet and find_network sdk calls to return the
 | 
				
			||||||
 | 
					        # values we want for this test
 | 
				
			||||||
 | 
					        self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6)
 | 
				
			||||||
 | 
					        self._network.id = self._subnet_ipv6.network_id
 | 
				
			||||||
 | 
					        self.network.find_network = mock.Mock(return_value=self._network)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        arglist = [
 | 
				
			||||||
 | 
					            self._subnet_ipv6.name,
 | 
				
			||||||
 | 
					            "--subnet-range", self._subnet_ipv6.cidr,
 | 
				
			||||||
 | 
					            "--network", self._subnet_ipv6.network_id,
 | 
				
			||||||
 | 
					            "--ip-version", str(self._subnet_ipv6.ip_version),
 | 
				
			||||||
 | 
					            "--ipv6-ra-mode", self._subnet_ipv6.ipv6_ra_mode,
 | 
				
			||||||
 | 
					            "--ipv6-address-mode", self._subnet_ipv6.ipv6_address_mode,
 | 
				
			||||||
 | 
					            "--gateway", self._subnet_ipv6.gateway_ip,
 | 
				
			||||||
 | 
					            "--dhcp",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for dns_addr in self._subnet_ipv6.dns_nameservers:
 | 
				
			||||||
 | 
					            arglist.append('--dns-nameserver')
 | 
				
			||||||
 | 
					            arglist.append(dns_addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for host_route in self._subnet_ipv6.host_routes:
 | 
				
			||||||
 | 
					            arglist.append('--host-route')
 | 
				
			||||||
 | 
					            value = 'gateway=' + host_route.get('nexthop', '') + \
 | 
				
			||||||
 | 
					                    ',destination=' + host_route.get('destination', '')
 | 
				
			||||||
 | 
					            arglist.append(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for pool in self._subnet_ipv6.allocation_pools:
 | 
				
			||||||
 | 
					            arglist.append('--allocation-pool')
 | 
				
			||||||
 | 
					            value = 'start=' + pool.get('start', '') + \
 | 
				
			||||||
 | 
					                    ',end=' + pool.get('end', '')
 | 
				
			||||||
 | 
					            arglist.append(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        verifylist = [
 | 
				
			||||||
 | 
					            ('name', self._subnet_ipv6.name),
 | 
				
			||||||
 | 
					            ('subnet_range', self._subnet_ipv6.cidr),
 | 
				
			||||||
 | 
					            ('network', self._subnet_ipv6.network_id),
 | 
				
			||||||
 | 
					            ('ip_version', self._subnet_ipv6.ip_version),
 | 
				
			||||||
 | 
					            ('ipv6_ra_mode', self._subnet_ipv6.ipv6_ra_mode),
 | 
				
			||||||
 | 
					            ('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode),
 | 
				
			||||||
 | 
					            ('gateway', self._subnet_ipv6.gateway_ip),
 | 
				
			||||||
 | 
					            ('dns_nameservers', self._subnet_ipv6.dns_nameservers),
 | 
				
			||||||
 | 
					            ('enable_dhcp', self._subnet_ipv6.enable_dhcp),
 | 
				
			||||||
 | 
					            ('host_routes', subnet_v2.convert_entries_to_gateway(
 | 
				
			||||||
 | 
					                self._subnet_ipv6.host_routes)),
 | 
				
			||||||
 | 
					            ('allocation_pools', self._subnet_ipv6.allocation_pools),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 | 
				
			||||||
 | 
					        columns, data = self.cmd.take_action(parsed_args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.network.create_subnet.assert_called_with(**{
 | 
				
			||||||
 | 
					            'cidr': self._subnet_ipv6.cidr,
 | 
				
			||||||
 | 
					            'dns_nameservers': self._subnet_ipv6.dns_nameservers,
 | 
				
			||||||
 | 
					            'enable_dhcp': self._subnet_ipv6.enable_dhcp,
 | 
				
			||||||
 | 
					            'gateway_ip': self._subnet_ipv6.gateway_ip,
 | 
				
			||||||
 | 
					            'host_routes': self._subnet_ipv6.host_routes,
 | 
				
			||||||
 | 
					            'ip_version': self._subnet_ipv6.ip_version,
 | 
				
			||||||
 | 
					            'ipv6_address_mode': self._subnet_ipv6.ipv6_address_mode,
 | 
				
			||||||
 | 
					            'ipv6_ra_mode': self._subnet_ipv6.ipv6_ra_mode,
 | 
				
			||||||
 | 
					            'name': self._subnet_ipv6.name,
 | 
				
			||||||
 | 
					            'network_id': self._subnet_ipv6.network_id,
 | 
				
			||||||
 | 
					            'allocation_pools': self._subnet_ipv6.allocation_pools,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        self.assertEqual(self.columns, columns)
 | 
				
			||||||
 | 
					        self.assertEqual(self.data_ipv6, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestDeleteSubnet(TestSubnet):
 | 
					class TestDeleteSubnet(TestSubnet):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # The subnet to delete.
 | 
					    # The subnet to delete.
 | 
				
			||||||
@@ -65,7 +395,7 @@ class TestListSubnet(TestSubnet):
 | 
				
			|||||||
        'ID',
 | 
					        'ID',
 | 
				
			||||||
        'Name',
 | 
					        'Name',
 | 
				
			||||||
        'Network',
 | 
					        'Network',
 | 
				
			||||||
        'Subnet'
 | 
					        'Subnet',
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    columns_long = columns + (
 | 
					    columns_long = columns + (
 | 
				
			||||||
        'Project',
 | 
					        'Project',
 | 
				
			||||||
@@ -74,7 +404,7 @@ class TestListSubnet(TestSubnet):
 | 
				
			|||||||
        'Allocation Pools',
 | 
					        'Allocation Pools',
 | 
				
			||||||
        'Host Routes',
 | 
					        'Host Routes',
 | 
				
			||||||
        'IP Version',
 | 
					        'IP Version',
 | 
				
			||||||
        'Gateway'
 | 
					        'Gateway',
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    data = []
 | 
					    data = []
 | 
				
			||||||
@@ -99,7 +429,7 @@ class TestListSubnet(TestSubnet):
 | 
				
			|||||||
            subnet_v2._format_allocation_pools(subnet.allocation_pools),
 | 
					            subnet_v2._format_allocation_pools(subnet.allocation_pools),
 | 
				
			||||||
            utils.format_list(subnet.host_routes),
 | 
					            utils.format_list(subnet.host_routes),
 | 
				
			||||||
            subnet.ip_version,
 | 
					            subnet.ip_version,
 | 
				
			||||||
            subnet.gateway_ip
 | 
					            subnet.gateway_ip,
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								releasenotes/notes/bug-1542364-5d1e93cfd24f0b65.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								releasenotes/notes/bug-1542364-5d1e93cfd24f0b65.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					features:
 | 
				
			||||||
 | 
					  - |
 | 
				
			||||||
 | 
					    Add ``subnet create`` command.
 | 
				
			||||||
 | 
					    [Bug `1542364 <https://bugs.launchpad.net/bugs/1542364>`_]
 | 
				
			||||||
@@ -351,6 +351,7 @@ openstack.network.v2 =
 | 
				
			|||||||
    security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule
 | 
					    security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule
 | 
				
			||||||
    security_group_rule_show = openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule
 | 
					    security_group_rule_show = openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subnet_create = openstackclient.network.v2.subnet:CreateSubnet
 | 
				
			||||||
    subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet
 | 
					    subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet
 | 
				
			||||||
    subnet_list = openstackclient.network.v2.subnet:ListSubnet
 | 
					    subnet_list = openstackclient.network.v2.subnet:ListSubnet
 | 
				
			||||||
    subnet_show = openstackclient.network.v2.subnet:ShowSubnet
 | 
					    subnet_show = openstackclient.network.v2.subnet:ShowSubnet
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user