LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File openstack-network-15-26.diff of Package python-openstackclient (Project home:garloff:OTC:python-save)

Index: python-openstackclient-1.5.0/openstackclient/network/common.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/network/common.py
+++ python-openstackclient-1.5.0/openstackclient/network/common.py
@@ -10,36 +10,207 @@
 #   License for the specific language governing permissions and limitations
 #   under the License.
 #
 
+import abc
+import six
+
+from openstackclient.common import command
 from openstackclient.common import exceptions
+from openstackclient.i18n import _
+
+
+@six.add_metaclass(abc.ABCMeta)
+class NetworkAndComputeCommand(command.Command):
+    """Network and Compute Command
+
+    Command class for commands that support implementation via
+    the network or compute endpoint. Such commands have different
+    implementations for take_action() and may even have different
+    arguments.
+    """
+
+    def take_action(self, parsed_args):
+        if self.app.client_manager.is_network_endpoint_enabled():
+            return self.take_action_network(self.app.client_manager.network,
+                                            parsed_args)
+        else:
+            return self.take_action_compute(self.app.client_manager.compute,
+                                            parsed_args)
+
+    def get_parser(self, prog_name):
+        self.log.debug('get_parser(%s)', prog_name)
+        parser = super(NetworkAndComputeCommand, self).get_parser(prog_name)
+        parser = self.update_parser_common(parser)
+        self.log.debug('common parser: %s', parser)
+        if self.app.client_manager.is_network_endpoint_enabled():
+            return self.update_parser_network(parser)
+        else:
+            return self.update_parser_compute(parser)
+
+    def update_parser_common(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    def update_parser_network(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    def update_parser_compute(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    @abc.abstractmethod
+    def take_action_network(self, client, parsed_args):
+        """Override to do something useful."""
+        pass
+
+    @abc.abstractmethod
+    def take_action_compute(self, client, parsed_args):
+        """Override to do something useful."""
+        pass
+
+
+@six.add_metaclass(abc.ABCMeta)
+class NetworkAndComputeDelete(NetworkAndComputeCommand):
+    """Network and Compute Delete
+
+    Delete class for commands that support implementation via
+    the network or compute endpoint. Such commands have different
+    implementations for take_action() and may even have different
+    arguments. This class supports bulk deletion, and error handling
+    following the rules in doc/source/command-errors.rst.
+    """
+
+    def take_action(self, parsed_args):
+        ret = 0
+        resources = getattr(parsed_args, self.resource, [])
+
+        for r in resources:
+            self.r = r
+            try:
+                if self.app.client_manager.is_network_endpoint_enabled():
+                    self.take_action_network(self.app.client_manager.network,
+                                             parsed_args)
+                else:
+                    self.take_action_compute(self.app.client_manager.compute,
+                                             parsed_args)
+            except Exception as e:
+                msg = _("Failed to delete %(resource)s with name or ID "
+                        "'%(name_or_id)s': %(e)s") % {
+                            "resource": self.resource,
+                            "name_or_id": r,
+                            "e": e,
+                }
+                self.app.log.error(msg)
+                ret += 1
 
+        if ret:
+            total = len(resources)
+            msg = _("%(num)s of %(total)s %(resource)s failed to delete.") % {
+                "num": ret,
+                "total": total,
+                "resource": self.resource,
+            }
+            raise exceptions.CommandError(msg)
 
-def find(client, resource, resources, name_or_id, name_attr='name'):
-    """Find a network resource
 
-    :param client: network client
-    :param resource: name of the resource
-    :param resources: plural name of resource
-    :param name_or_id: name or id of resource user is looking for
-    :param name_attr: key to the name attribute for the resource
+@six.add_metaclass(abc.ABCMeta)
+class NetworkAndComputeLister(command.Lister):
+    """Network and Compute Lister
 
-    For example:
-        n = find(netclient, 'network', 'networks', 'matrix')
+    Lister class for commands that support implementation via
+    the network or compute endpoint. Such commands have different
+    implementations for take_action() and may even have different
+    arguments.
     """
-    list_method = getattr(client, "list_%s" % resources)
-    # Search for by name
-    kwargs = {name_attr: name_or_id, 'fields': 'id'}
-    data = list_method(**kwargs)
-    info = data[resources]
-    if len(info) == 1:
-        return info[0]['id']
-    if len(info) > 1:
-        msg = "More than one %s exists with the name '%s'."
-        raise exceptions.CommandError(msg % (resource, name_or_id))
-    # Search for by id
-    data = list_method(id=name_or_id, fields='id')
-    info = data[resources]
-    if len(info) == 1:
-        return info[0]['id']
-    msg = "No %s with a name or ID of '%s' exists." % (resource, name_or_id)
-    raise exceptions.CommandError(msg)
+
+    def take_action(self, parsed_args):
+        if self.app.client_manager.is_network_endpoint_enabled():
+            return self.take_action_network(self.app.client_manager.network,
+                                            parsed_args)
+        else:
+            return self.take_action_compute(self.app.client_manager.compute,
+                                            parsed_args)
+
+    def get_parser(self, prog_name):
+        self.log.debug('get_parser(%s)', prog_name)
+        parser = super(NetworkAndComputeLister, self).get_parser(prog_name)
+        parser = self.update_parser_common(parser)
+        self.log.debug('common parser: %s', parser)
+        if self.app.client_manager.is_network_endpoint_enabled():
+            return self.update_parser_network(parser)
+        else:
+            return self.update_parser_compute(parser)
+
+    def update_parser_common(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    def update_parser_network(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    def update_parser_compute(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    @abc.abstractmethod
+    def take_action_network(self, client, parsed_args):
+        """Override to do something useful."""
+        pass
+
+    @abc.abstractmethod
+    def take_action_compute(self, client, parsed_args):
+        """Override to do something useful."""
+        pass
+
+
+@six.add_metaclass(abc.ABCMeta)
+class NetworkAndComputeShowOne(command.ShowOne):
+    """Network and Compute ShowOne
+
+    ShowOne class for commands that support implementation via
+    the network or compute endpoint. Such commands have different
+    implementations for take_action() and may even have different
+    arguments.
+    """
+
+    def take_action(self, parsed_args):
+        if self.app.client_manager.is_network_endpoint_enabled():
+            return self.take_action_network(self.app.client_manager.network,
+                                            parsed_args)
+        else:
+            return self.take_action_compute(self.app.client_manager.compute,
+                                            parsed_args)
+
+    def get_parser(self, prog_name):
+        self.log.debug('get_parser(%s)', prog_name)
+        parser = super(NetworkAndComputeShowOne, self).get_parser(prog_name)
+        parser = self.update_parser_common(parser)
+        self.log.debug('common parser: %s', parser)
+        if self.app.client_manager.is_network_endpoint_enabled():
+            return self.update_parser_network(parser)
+        else:
+            return self.update_parser_compute(parser)
+
+    def update_parser_common(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    def update_parser_network(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    def update_parser_compute(self, parser):
+        """Default is no updates to parser."""
+        return parser
+
+    @abc.abstractmethod
+    def take_action_network(self, client, parsed_args):
+        """Override to do something useful."""
+        pass
+
+    @abc.abstractmethod
+    def take_action_compute(self, client, parsed_args):
+        """Override to do something useful."""
+        pass
Index: python-openstackclient-1.5.0/openstackclient/network/utils.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/utils.py
@@ -0,0 +1,41 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+
+# Transform compute security group rule for display.
+def transform_compute_security_group_rule(sg_rule):
+    info = {}
+    info.update(sg_rule)
+    from_port = info.pop('from_port')
+    to_port = info.pop('to_port')
+    if isinstance(from_port, int) and isinstance(to_port, int):
+        port_range = {'port_range': "%u:%u" % (from_port, to_port)}
+    elif from_port is None and to_port is None:
+        port_range = {'port_range': ""}
+    else:
+        port_range = {'port_range': "%s:%s" % (from_port, to_port)}
+    info.update(port_range)
+    if 'cidr' in info['ip_range']:
+        info['ip_range'] = info['ip_range']['cidr']
+    else:
+        info['ip_range'] = ''
+    if info['ip_protocol'] is None:
+        info['ip_protocol'] = ''
+    elif info['ip_protocol'].lower() == 'icmp':
+        info['port_range'] = ''
+    group = info.pop('group')
+    if 'name' in group:
+        info['remote_security_group'] = group['name']
+    else:
+        info['remote_security_group'] = ''
+    return info
Index: python-openstackclient-1.5.0/openstackclient/network/v2/address_scope.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/address_scope.py
@@ -0,0 +1,230 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""Address scope action implementations"""
+
+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
+
+
+def _get_columns(item):
+    columns = list(item.keys())
+    if 'tenant_id' in columns:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+
+    return tuple(sorted(columns))
+
+
+def _get_attrs(client_manager, parsed_args):
+    attrs = {}
+    attrs['name'] = parsed_args.name
+    attrs['ip_version'] = parsed_args.ip_version
+    if parsed_args.share:
+        attrs['shared'] = True
+    if parsed_args.no_share:
+        attrs['shared'] = False
+    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
+
+
+class CreateAddressScope(command.ShowOne):
+    """Create a new Address Scope"""
+
+    def get_parser(self, prog_name):
+        parser = super(CreateAddressScope, self).get_parser(prog_name)
+        parser.add_argument(
+            'name',
+            metavar="<name>",
+            help=_("New address scope name")
+        )
+        parser.add_argument(
+            '--ip-version',
+            type=int,
+            default=4,
+            choices=[4, 6],
+            help=_("IP version (default is 4)")
+        )
+        parser.add_argument(
+            '--project',
+            metavar="<project>",
+            help=_("Owner's project (name or ID)")
+        )
+        identity_common.add_project_domain_option_to_parser(parser)
+
+        share_group = parser.add_mutually_exclusive_group()
+        share_group.add_argument(
+            '--share',
+            action='store_true',
+            help=_('Share the address scope between projects')
+        )
+        share_group.add_argument(
+            '--no-share',
+            action='store_true',
+            help=_('Do not share the address scope between projects (default)')
+        )
+
+        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_address_scope(**attrs)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters={})
+
+        return columns, data
+
+
+class DeleteAddressScope(command.Command):
+    """Delete address scope(s)"""
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteAddressScope, self).get_parser(prog_name)
+        parser.add_argument(
+            'address_scope',
+            metavar="<address-scope>",
+            nargs='+',
+            help=_("Address scope(s) to delete (name or ID)")
+        )
+
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        result = 0
+
+        for scope in parsed_args.address_scope:
+            try:
+                obj = client.find_address_scope(scope, ignore_missing=False)
+                client.delete_address_scope(obj)
+            except Exception as e:
+                result += 1
+                self.app.log.error(_("Failed to delete address scope with "
+                                   "name or ID '%(scope)s': %(e)s")
+                                   % {'scope': scope, 'e': e})
+
+        if result > 0:
+            total = len(parsed_args.address_scope)
+            msg = (_("%(result)s of %(total)s address scopes failed "
+                   "to delete.") % {'result': result, 'total': total})
+            raise exceptions.CommandError(msg)
+
+
+class ListAddressScope(command.Lister):
+    """List address scopes"""
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        columns = (
+            'id',
+            'name',
+            'ip_version',
+            'shared',
+            'tenant_id',
+        )
+        column_headers = (
+            'ID',
+            'Name',
+            'IP Version',
+            'Shared',
+            'Project',
+        )
+        data = client.address_scopes()
+
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns, formatters={},
+                ) for s in data))
+
+
+class SetAddressScope(command.Command):
+    """Set address scope properties"""
+
+    def get_parser(self, prog_name):
+        parser = super(SetAddressScope, self).get_parser(prog_name)
+        parser.add_argument(
+            'address_scope',
+            metavar="<address-scope>",
+            help=_("Address scope to modify (name or ID)")
+        )
+        parser.add_argument(
+            '--name',
+            metavar="<name>",
+            help=_('Set address scope name')
+        )
+        share_group = parser.add_mutually_exclusive_group()
+        share_group.add_argument(
+            '--share',
+            action='store_true',
+            help=_('Share the address scope between projects')
+        )
+        share_group.add_argument(
+            '--no-share',
+            action='store_true',
+            help=_('Do not share the address scope between projects')
+        )
+
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_address_scope(
+            parsed_args.address_scope,
+            ignore_missing=False)
+        attrs = {}
+        if parsed_args.name is not None:
+            attrs['name'] = parsed_args.name
+        if parsed_args.share:
+            attrs['shared'] = True
+        if parsed_args.no_share:
+            attrs['shared'] = False
+        if attrs == {}:
+            msg = _("Nothing specified to be set.")
+            raise exceptions.CommandError(msg)
+        client.update_address_scope(obj, **attrs)
+
+
+class ShowAddressScope(command.ShowOne):
+    """Display address scope details"""
+
+    def get_parser(self, prog_name):
+        parser = super(ShowAddressScope, self).get_parser(prog_name)
+        parser.add_argument(
+            'address_scope',
+            metavar="<address-scope>",
+            help=_("Address scope to display (name or ID)")
+        )
+
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_address_scope(
+            parsed_args.address_scope,
+            ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters={})
+
+        return columns, data
Index: python-openstackclient-1.5.0/openstackclient/network/v2/floating_ip.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/floating_ip.py
@@ -0,0 +1,211 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""IP Floating action implementations"""
+
+from openstackclient.common import utils
+from openstackclient.i18n import _
+from openstackclient.network import common
+
+
+def _get_columns(item):
+    columns = list(item.keys())
+    if 'tenant_id' in columns:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    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"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'floating_ip',
+            metavar="<floating-ip>",
+            help=_("Floating IP to delete (IP address or ID)")
+        )
+        return parser
+
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_ip(parsed_args.floating_ip)
+        client.delete_ip(obj)
+
+    def take_action_compute(self, client, parsed_args):
+        obj = utils.find_resource(
+            client.floating_ips,
+            parsed_args.floating_ip,
+        )
+        client.floating_ips.delete(obj.id)
+
+
+class ListFloatingIP(common.NetworkAndComputeLister):
+    """List floating IP(s)"""
+
+    def take_action_network(self, client, parsed_args):
+        columns = (
+            'id',
+            'floating_ip_address',
+            'fixed_ip_address',
+            'port_id',
+        )
+        headers = (
+            'ID',
+            'Floating IP Address',
+            'Fixed IP Address',
+            'Port',
+        )
+
+        query = {}
+        #data = client.ips(**query)
+        data = client.api.fip_list()
+
+        return (headers,
+                (utils.get_dict_properties(
+                    s, columns,
+                    formatters={},
+                ) for s in data))
+
+    def take_action_compute(self, client, parsed_args):
+        columns = (
+            'ID',
+            'IP',
+            'Fixed IP',
+            'Instance ID',
+            'Pool',
+        )
+        headers = (
+            'ID',
+            'Floating IP Address',
+            'Fixed IP Address',
+            'Server',
+            'Pool',
+        )
+
+        data = client.floating_ips.list()
+
+        return (headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters={},
+                ) for s in data))
+
+
+class ShowFloatingIP(common.NetworkAndComputeShowOne):
+    """Show floating IP details"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'floating_ip',
+            metavar="<floating-ip>",
+            help=_("Floating IP to display (IP address or ID)")
+        )
+        return parser
+
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_ip(parsed_args.floating_ip, ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns)
+        return (columns, data)
+
+    def take_action_compute(self, client, parsed_args):
+        obj = utils.find_resource(
+            client.floating_ips,
+            parsed_args.floating_ip,
+        )
+        columns = _get_columns(obj._info)
+        data = utils.get_dict_properties(obj._info, columns)
+        return (columns, data)
Index: python-openstackclient-1.5.0/openstackclient/network/v2/ip_availability.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/ip_availability.py
@@ -0,0 +1,110 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""IP Availability Info implementations"""
+
+from openstackclient.common import command
+from openstackclient.common import utils
+from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
+
+
+_formatters = {
+    'subnet_ip_availability': utils.format_list_of_dicts,
+}
+
+
+def _get_columns(item):
+    columns = list(item.keys())
+    if 'tenant_id' in columns:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    return tuple(sorted(columns))
+
+
+class ListIPAvailability(command.Lister):
+    """List IP availability for network"""
+
+    def get_parser(self, prog_name):
+        parser = super(ListIPAvailability, self).get_parser(prog_name)
+        parser.add_argument(
+            '--ip-version',
+            type=int,
+            choices=[4, 6],
+            metavar='<ip-version>',
+            dest='ip_version',
+            help=_("List IP availability of given IP version networks"),
+        )
+        parser.add_argument(
+            '--project',
+            metavar='<project>',
+            help=_("List IP availability of given project"),
+        )
+        identity_common.add_project_domain_option_to_parser(parser)
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+
+        columns = (
+            'network_id',
+            'network_name',
+            'total_ips',
+            'used_ips',
+        )
+        column_headers = (
+            'Network ID',
+            'Network Name',
+            'Total IPs',
+            'Used IPs',
+        )
+
+        filters = {}
+        if parsed_args.ip_version:
+            filters['ip_version'] = parsed_args.ip_version
+
+        if parsed_args.project:
+            identity_client = self.app.client_manager.identity
+            project_id = identity_common.find_project(
+                identity_client,
+                parsed_args.project,
+                parsed_args.project_domain,
+            ).id
+            filters['tenant_id'] = project_id
+        #data = client.network_ip_availabilities(**filters)
+        data = client.api.avail_list(**filters)
+        return (column_headers,
+                (utils.get_dict_properties(
+                    s, columns,
+                ) for s in data))
+
+
+class ShowIPAvailability(command.ShowOne):
+    """Show network IP availability details"""
+
+    def get_parser(self, prog_name):
+        parser = super(ShowIPAvailability, self).get_parser(prog_name)
+        parser.add_argument(
+            'network',
+            metavar="<network>",
+            help=_("Show IP availability for a specific network (name or ID)"),
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_network_ip_availability(parsed_args.network,
+                                                  ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+        return columns, data
Index: python-openstackclient-1.5.0/openstackclient/network/v2/network.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/network/v2/network.py
+++ python-openstackclient-1.5.0/openstackclient/network/v2/network.py
@@ -12,305 +12,458 @@
 #
 
 """Network action implementations"""
 
-import logging
-import six
-
-from cliff import command
-from cliff import lister
-from cliff import show
-
+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
 
 
-def _prep_network_detail(net):
-    """Prepare network object for output"""
+def _format_admin_state(item):
+    return 'UP' if item else 'DOWN'
 
-    if 'subnets' in net:
-        net['subnets'] = utils.format_list(net['subnets'])
-    if 'admin_state_up' in net:
-        net['state'] = 'UP' if net['admin_state_up'] else 'DOWN'
-        net.pop('admin_state_up')
-    if 'router:external' in net:
-        net['router_type'] = 'External' if net['router:external'] \
-            else 'Internal'
-        net.pop('router:external')
-    if 'tenant_id' in net:
-        net['project_id'] = net.pop('tenant_id')
-    return net
 
+def _format_router_external(item):
+    return 'External' if item else 'Internal'
 
-class CreateNetwork(show.ShowOne):
-    """Create new network"""
 
-    log = logging.getLogger(__name__ + '.CreateNetwork')
+_formatters = {
+    'subnets': utils.format_list,
+    'admin_state_up': _format_admin_state,
+    'router:external': _format_router_external,
+    'availability_zones': utils.format_list,
+    'availability_zone_hints': utils.format_list,
+}
+
+
+def _get_columns(item):
+    columns = list(item.keys())
+    if 'tenant_id' in columns:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    return tuple(sorted(columns))
+
+
+def _get_attrs(client_manager, parsed_args):
+    attrs = {}
+    if parsed_args.name is not None:
+        attrs['name'] = str(parsed_args.name)
+    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:
+        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
+
+    # "network set" command doesn't support setting availability zone hints.
+    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
+
+    # 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
+    # Update VLAN Transparency for networks
+    if parsed_args.transparent_vlan:
+        attrs['vlan_transparent'] = True
+    if parsed_args.no_transparent_vlan:
+        attrs['vlan_transparent'] = False
+    return attrs
+
+
+def _add_additional_network_options(parser):
+    # Add additional 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"))
+
+    vlan_transparent_grp = parser.add_mutually_exclusive_group()
+    vlan_transparent_grp.add_argument(
+        '--transparent-vlan',
+        action='store_true',
+        help=_("Make the network VLAN transparent"))
+    vlan_transparent_grp.add_argument(
+        '--no-transparent-vlan',
+        action='store_true',
+        help=_("Do not make the network VLAN transparent"))
+
+
+def _get_attrs_compute(client_manager, parsed_args):
+    attrs = {}
+    if parsed_args.name is not None:
+        attrs['label'] = str(parsed_args.name)
+    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
 
-    def get_parser(self, prog_name):
-        parser = super(CreateNetwork, self).get_parser(prog_name)
+
+class CreateNetwork(common.NetworkAndComputeShowOne):
+    """Create new network"""
+
+    def update_parser_common(self, parser):
         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',
+            action='store_true',
+            default=None,
+            help=_("Share the network between projects")
+        )
+        share_group.add_argument(
+            '--no-share',
+            action='store_true',
+            help=_("Do not share the network between projects")
+        )
+        return parser
+
+    def update_parser_network(self, parser):
         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',
-        )
-        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',
-        )
-        share_group.add_argument(
-            '--no-share',
-            dest='shared',
-            action='store_false',
-            help='Do not share the network between projects',
+            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(
-            '--domain',
-            metavar='<domain>',
-            help="Owner's domain (name or ID)")
+            '--availability-zone-hint',
+            action='append',
+            dest='availability_zone_hints',
+            metavar='<availability-zone>',
+            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_additional_network_options(parser)
         return parser
 
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)' % parsed_args)
-        client = self.app.client_manager.network
-        body = self.get_body(parsed_args)
-        create_method = getattr(client, "create_network")
-        data = create_method(body)['network']
-        if data:
-            data = _prep_network_detail(data)
-        else:
-            data = {'': ''}
-        return zip(*sorted(six.iteritems(data)))
+    def update_parser_compute(self, parser):
+        parser.add_argument(
+            '--subnet',
+            metavar='<subnet>',
+            help=_("IPv4 subnet for fixed IPs (in CIDR notation)")
+        )
+        return parser
 
-    def get_body(self, parsed_args):
-        body = {'name': str(parsed_args.name),
-                'admin_state_up': parsed_args.admin_state}
-        if parsed_args.shared is not None:
-            body['shared'] = parsed_args.shared
-        if parsed_args.project is not None:
-            identity_client = self.app.client_manager.identity
-            if parsed_args.domain is not None:
-                domain = identity_common.find_domain(identity_client,
-                                                     parsed_args.domain)
-                project_id = utils.find_resource(identity_client.projects,
-                                                 parsed_args.project,
-                                                 domain_id=domain.id).id
-            else:
-                project_id = utils.find_resource(identity_client.projects,
-                                                 parsed_args.project).id
-            body['tenant_id'] = project_id
-        return {'network': body}
+    def take_action_network(self, client, parsed_args):
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
+        obj = client.create_network(**attrs)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+        return (columns, data)
+
+    def take_action_compute(self, client, parsed_args):
+        attrs = _get_attrs_compute(self.app.client_manager, parsed_args)
+        obj = client.networks.create(**attrs)
+        columns = _get_columns(obj._info)
+        data = utils.get_dict_properties(obj._info, columns)
+        return (columns, data)
 
 
-class DeleteNetwork(command.Command):
+class DeleteNetwork(common.NetworkAndComputeDelete):
     """Delete network(s)"""
 
-    log = logging.getLogger(__name__ + '.DeleteNetwork')
+    # Used by base class to find resources in parsed_args.
+    resource = 'network'
+    r = None
 
-    def get_parser(self, prog_name):
-        parser = super(DeleteNetwork, self).get_parser(prog_name)
+    def update_parser_common(self, parser):
         parser.add_argument(
-            'networks',
+            'network',
             metavar="<network>",
             nargs="+",
-            help=("Network to delete (name or ID)")
+            help=_("Network(s) to delete (name or ID)")
         )
+
         return parser
 
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)' % parsed_args)
-        client = self.app.client_manager.network
-        delete_method = getattr(client, "delete_network")
-        for network in parsed_args.networks:
-            _id = common.find(client, 'network', 'networks', network)
-            delete_method(_id)
-        return
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_network(self.r, ignore_missing=False)
+        client.delete_network(obj)
 
+    def take_action_compute(self, client, parsed_args):
+        network = utils.find_resource(client.networks, self.r)
+        client.networks.delete(network.id)
 
-class ListNetwork(lister.Lister):
-    """List networks"""
 
-    log = logging.getLogger(__name__ + '.ListNetwork')
+class ListNetwork(common.NetworkAndComputeLister):
+    """List networks"""
 
-    def get_parser(self, prog_name):
-        parser = super(ListNetwork, self).get_parser(prog_name)
+    def update_parser_common(self, parser):
         parser.add_argument(
             '--external',
             action='store_true',
             default=False,
-            help='List external networks',
+            help=_("List external networks")
         )
         parser.add_argument(
-            '--dhcp',
-            metavar='<dhcp-id>',
-            help='DHCP agent ID')
-        parser.add_argument(
             '--long',
             action='store_true',
             default=False,
-            help='List additional fields in output',
+            help=_("List additional fields in output")
         )
         return parser
 
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)' % parsed_args)
-        client = self.app.client_manager.network
-
-        if parsed_args.dhcp:
-            data = client.api.dhcp_agent_list(dhcp_id=parsed_args.dhcp)
-
-            columns = ('ID',)
-            column_headers = columns
+    def take_action_network(self, client, parsed_args):
+        if parsed_args.long:
+            columns = (
+                'id',
+                'name',
+                'status',
+                'tenant_id',
+                'admin_state_up',
+                'shared',
+                'subnets',
+                'provider_network_type',
+                'router:external',
+                'availability_zones',
+            )
+            column_headers = (
+                'ID',
+                'Name',
+                'Status',
+                'Project',
+                'State',
+                'Shared',
+                'Subnets',
+                'Network Type',
+                'Router Type',
+                'Availability Zones',
+            )
         else:
-            data = client.api.network_list(external=parsed_args.external)
+            columns = (
+                'id',
+                'name',
+                'subnets'
+            )
+            column_headers = (
+                'ID',
+                'Name',
+                'Subnets',
+            )
 
-            if parsed_args.long:
-                columns = (
-                    'ID',
-                    'Name',
-                    'Status',
-                    'project_id',
-                    'state',
-                    'Shared',
-                    'Subnets',
-                    'provider:network_type',
-                    'router_type',
-                )
-                column_headers = (
-                    'ID',
-                    'Name',
-                    'Status',
-                    'Project',
-                    'State',
-                    'Shared',
-                    'Subnets',
-                    'Network Type',
-                    'Router Type',
-                )
-            else:
-                columns = ('ID', 'Name', 'Subnets')
-                column_headers = columns
+        if parsed_args.external:
+            args = {'router:external': True}
+        else:
+            args = {}
 
-        for d in data:
-            d = _prep_network_detail(d)
+        #data = client.networks(**args)
+	data = client.api.network_list(external=parsed_args.external)
 
         return (column_headers,
                 (utils.get_dict_properties(
                     s, columns,
-                    formatters={'subnets': utils.format_list},
+                    formatters=_formatters,
+                ) for s in data))
+
+    def take_action_compute(self, client, parsed_args):
+        columns = (
+            'id',
+            'label',
+            'cidr',
+        )
+        column_headers = (
+            'ID',
+            'Name',
+            'Subnet',
+        )
+
+        data = client.networks.list()
+
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters=_formatters,
                 ) for s in data))
 
 
 class SetNetwork(command.Command):
     """Set network properties"""
 
-    log = logging.getLogger(__name__ + '.SetNetwork')
-
     def get_parser(self, prog_name):
         parser = super(SetNetwork, self).get_parser(prog_name)
         parser.add_argument(
-            'identifier',
+            '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_additional_network_options(parser)
         return parser
 
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)' % parsed_args)
         client = self.app.client_manager.network
-        _id = common.find(client, 'network', 'networks',
-                          parsed_args.identifier)
-        body = {}
-        if parsed_args.name is not None:
-            body['name'] = str(parsed_args.name)
-        if parsed_args.admin_state is not None:
-            body['admin_state_up'] = parsed_args.admin_state
-        if parsed_args.shared is not None:
-            body['shared'] = parsed_args.shared
-        if body == {}:
-            msg = "Nothing specified to be set"
+        obj = client.find_network(parsed_args.network, ignore_missing=False)
+
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
+        if attrs == {}:
+            msg = _("Nothing specified to be set")
             raise exceptions.CommandError(msg)
-        update_method = getattr(client, "update_network")
-        update_method(_id, {'network': body})
-        return
 
+        client.update_network(obj, **attrs)
 
-class ShowNetwork(show.ShowOne):
-    """Show network details"""
 
-    log = logging.getLogger(__name__ + '.ShowNetwork')
+class ShowNetwork(common.NetworkAndComputeShowOne):
+    """Show network details"""
 
-    def get_parser(self, prog_name):
-        parser = super(ShowNetwork, self).get_parser(prog_name)
+    def update_parser_common(self, parser):
         parser.add_argument(
-            'identifier',
+            'network',
             metavar="<network>",
-            help=("Network to display (name or ID)")
+            help=_("Network to display (name or ID)")
         )
         return parser
 
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)' % parsed_args)
-        client = self.app.client_manager.network
-        net = client.api.find_attr(
-            'networks',
-            parsed_args.identifier,
-        )
-        data = _prep_network_detail(net)
-        return zip(*sorted(six.iteritems(data)))
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_network(parsed_args.network, ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+        return (columns, data)
+
+    def take_action_compute(self, client, parsed_args):
+        obj = utils.find_resource(
+            client.networks,
+            parsed_args.network,
+        )
+        columns = _get_columns(obj._info)
+        data = utils.get_dict_properties(obj._info, columns)
+        return (columns, data)
Index: python-openstackclient-1.5.0/openstackclient/network/v2/network_segment.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/network_segment.py
@@ -0,0 +1,116 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""Network segment action implementations"""
+
+# TODO(rtheis): Add description and name properties when support is available.
+
+from openstackclient.common import command
+from openstackclient.common import utils
+from openstackclient.i18n import _
+
+
+class ListNetworkSegment(command.Lister):
+    """List network segments
+
+       (Caution: This is a beta command and subject to change.
+        Use global option --os-beta-command to enable
+        this command)
+    """
+
+    def get_parser(self, prog_name):
+        parser = super(ListNetworkSegment, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help=_('List additional fields in output'),
+        )
+        parser.add_argument(
+            '--network',
+            metavar='<network>',
+            help=_('List network segments that belong to this '
+                   'network (name or ID)'),
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.validate_os_beta_command_enabled()
+
+        network_client = self.app.client_manager.network
+
+        filters = {}
+        if parsed_args.network:
+            _network = network_client.find_network(
+                parsed_args.network,
+                ignore_missing=False
+            )
+            filters = {'network_id': _network.id}
+        #data = network_client.segments(**filters)
+	data = network_client.api.segmet_list(**filters)
+
+        headers = (
+            'ID',
+            'Network',
+            'Network Type',
+            'Segment',
+        )
+        columns = (
+            'id',
+            'network_id',
+            'network_type',
+            'segmentation_id',
+        )
+        if parsed_args.long:
+            headers = headers + (
+                'Physical Network',
+            )
+            columns = columns + (
+                'physical_network',
+            )
+
+        return (headers,
+                (utils.get_dict_properties(
+                    s, columns,
+                    formatters={},
+                ) for s in data))
+
+
+class ShowNetworkSegment(command.ShowOne):
+    """Display network segment details
+
+       (Caution: This is a beta command and subject to change.
+        Use global option --os-beta-command to enable
+        this command)
+    """
+
+    def get_parser(self, prog_name):
+        parser = super(ShowNetworkSegment, self).get_parser(prog_name)
+        parser.add_argument(
+            'network_segment',
+            metavar='<network-segment>',
+            help=_('Network segment to display (ID only)'),
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.validate_os_beta_command_enabled()
+
+        client = self.app.client_manager.network
+        obj = client.find_segment(
+            parsed_args.network_segment,
+            ignore_missing=False
+        )
+        columns = tuple(sorted(obj.keys()))
+        data = utils.get_item_properties(obj, columns)
+        return (columns, data)
Index: python-openstackclient-1.5.0/openstackclient/network/v2/port.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/port.py
@@ -0,0 +1,453 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""Port action implementations"""
+
+import argparse
+import logging
+
+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
+
+
+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,
+    'binding_profile': utils.format_dict,
+    'binding_vif_details': utils.format_dict,
+    'dns_assignment': utils.format_list_of_dicts,
+    'extra_dhcp_opts': utils.format_list_of_dicts,
+    'fixed_ips': utils.format_list_of_dicts,
+    'security_groups': utils.format_list,
+}
+
+
+def _get_columns(item):
+    columns = list(item.keys())
+    if 'tenant_id' in columns:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    binding_columns = [
+        'binding:host_id',
+        'binding:profile',
+        'binding:vif_details',
+        'binding:vif_type',
+        'binding:vnic_type',
+    ]
+    for binding_column in binding_columns:
+        if binding_column in columns:
+            columns.remove(binding_column)
+            columns.append(binding_column.replace('binding:', 'binding_', 1))
+    return tuple(sorted(columns))
+
+
+def _get_attrs(client_manager, parsed_args):
+    attrs = {}
+
+    # Handle deprecated options
+    # NOTE(dtroyer): --device-id and --host-id were deprecated in Mar 2016.
+    #                Do not remove before 3.x release or Mar 2017.
+    if parsed_args.device_id:
+        attrs['device_id'] = parsed_args.device_id
+        LOG.warning(_(
+            'The --device-id option is deprecated, '
+            'please use --device instead.'
+        ))
+    if parsed_args.host_id:
+        attrs['binding:host_id'] = parsed_args.host_id
+        LOG.warning(_(
+            'The --host-id option is deprecated, '
+            'please use --host instead.'
+        ))
+
+    if parsed_args.fixed_ip is not None:
+        attrs['fixed_ips'] = parsed_args.fixed_ip
+    if parsed_args.device:
+        attrs['device_id'] = parsed_args.device
+    if parsed_args.device_owner is not None:
+        attrs['device_owner'] = parsed_args.device_owner
+    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:
+        attrs['binding:vnic_type'] = parsed_args.vnic_type
+    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 '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:
+        attrs['network_id'] = parsed_args.network
+    if 'project' in parsed_args and parsed_args.project is not None:
+        # TODO(singhj): since 'project' logic is common among
+        # router, network, port etc., maybe move it to a common file.
+        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
+
+
+def _prepare_fixed_ips(client_manager, parsed_args):
+    """Fix and properly format fixed_ip option.
+
+    Appropriately convert any subnet names to their respective ids.
+    Convert fixed_ips in parsed args to be in valid dictionary format:
+    {'subnet': 'foo'}.
+    """
+    client = client_manager.network
+    ips = []
+
+    if parsed_args.fixed_ip:
+        for ip_spec in parsed_args.fixed_ip:
+            if 'subnet' in ip_spec:
+                subnet_name_id = ip_spec['subnet']
+                if subnet_name_id:
+                    _subnet = client.find_subnet(subnet_name_id,
+                                                 ignore_missing=False)
+                    ip_spec['subnet_id'] = _subnet.id
+                    del ip_spec['subnet']
+
+            if 'ip-address' in ip_spec:
+                ip_spec['ip_address'] = ip_spec['ip-address']
+                del ip_spec['ip-address']
+
+            ips.append(ip_spec)
+
+    if ips:
+        parsed_args.fixed_ip = ips
+
+
+def _add_updatable_args(parser):
+        # 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")
+        )
+        device_group.add_argument(
+            '--device-id',
+            metavar='<device-id>',
+            help=argparse.SUPPRESS,
+        )
+        parser.add_argument(
+            '--device-owner',
+            metavar='<device-owner>',
+            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, 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)")
+        )
+        host_group.add_argument(
+            '--host-id',
+            metavar='<host-id>',
+            help=argparse.SUPPRESS,
+        )
+
+
+class CreatePort(command.ShowOne):
+    """Create a new port"""
+
+    def get_parser(self, prog_name):
+        parser = super(CreatePort, self).get_parser(prog_name)
+
+        parser.add_argument(
+            '--network',
+            metavar='<network>',
+            required=True,
+            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',
+            action='store_true',
+            default=True,
+            help=_("Enable port (default)")
+        )
+        admin_group.add_argument(
+            '--disable',
+            action='store_true',
+            help=_("Disable port")
+        )
+        parser.add_argument(
+            '--mac-address',
+            metavar='<mac-address>',
+            help=_("MAC address of this port")
+        )
+        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(
+            'name',
+            metavar='<name>',
+            help=_("Name of this port")
+        )
+        # TODO(singhj): Add support for extended options:
+        # qos,security groups,dhcp, address pairs
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        _network = client.find_network(parsed_args.network,
+                                       ignore_missing=False)
+        parsed_args.network = _network.id
+        _prepare_fixed_ips(self.app.client_manager, parsed_args)
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
+        obj = client.create_port(**attrs)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+
+        return columns, data
+
+
+class DeletePort(command.Command):
+    """Delete port(s)"""
+
+    def get_parser(self, prog_name):
+        parser = super(DeletePort, self).get_parser(prog_name)
+        parser.add_argument(
+            'port',
+            metavar="<port>",
+            nargs="+",
+            help=_("Port(s) to delete (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        result = 0
+
+        for port in parsed_args.port:
+            try:
+                obj = client.find_port(port, ignore_missing=False)
+                client.delete_port(obj)
+            except Exception as e:
+                result += 1
+                self.app.log.error(_("Failed to delete port with "
+                                   "name or ID '%(port)s': %(e)s")
+                                   % {'port': port, 'e': e})
+
+        if result > 0:
+            total = len(parsed_args.port)
+            msg = (_("%(result)s of %(total)s ports failed "
+                   "to delete.") % {'result': result, 'total': total})
+            raise exceptions.CommandError(msg)
+
+
+class ListPort(command.Lister):
+    """List ports"""
+
+    def get_parser(self, prog_name):
+        parser = super(ListPort, self).get_parser(prog_name)
+        parser.add_argument(
+            '--router',
+            metavar='<router>',
+            dest='router',
+            help=_("List only ports attached to this router (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+
+        columns = (
+            'id',
+            'name',
+            'mac_address',
+            'fixed_ips',
+        )
+        column_headers = (
+            'ID',
+            'Name',
+            'MAC Address',
+            'Fixed IP Addresses',
+        )
+
+        filters = {}
+        if parsed_args.router:
+            _router = client.find_router(parsed_args.router,
+                                         ignore_missing=False)
+            filters = {'device_id': _router.id}
+
+        #data = client.ports(**filters)
+        data = client.api.port_list(**filters)
+
+        return (column_headers,
+                (utils.get_dict_properties(
+                    s, columns,
+                    formatters=_formatters,
+                ) for s in data))
+
+
+class SetPort(command.Command):
+    """Set port properties"""
+
+    def get_parser(self, prog_name):
+        parser = super(SetPort, self).get_parser(prog_name)
+        _add_updatable_args(parser)
+        admin_group = parser.add_mutually_exclusive_group()
+        admin_group.add_argument(
+            '--enable',
+            action='store_true',
+            default=None,
+            help=_("Enable port")
+        )
+        admin_group.add_argument(
+            '--disable',
+            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)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+
+        _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)
+        client.update_port(obj, **attrs)
+
+
+class ShowPort(command.ShowOne):
+    """Display port details"""
+
+    def get_parser(self, prog_name):
+        parser = super(ShowPort, self).get_parser(prog_name)
+        parser.add_argument(
+            'port',
+            metavar="<port>",
+            help=_("Port to display (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_port(parsed_args.port, ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+        return columns, data
Index: python-openstackclient-1.5.0/openstackclient/network/v2/router.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/router.py
@@ -0,0 +1,455 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""Router action implementations"""
+
+import argparse
+import json
+import logging
+
+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
+
+
+LOG = logging.getLogger(__name__)
+
+
+def _format_admin_state(state):
+    return 'UP' if state else 'DOWN'
+
+
+def _format_external_gateway_info(info):
+    try:
+        return json.dumps(info)
+    except (TypeError, KeyError):
+        return ''
+
+
+def _format_routes(routes):
+    # Map the route keys to match --route option.
+    for route in routes:
+        if 'nexthop' in route:
+            route['gateway'] = route.pop('nexthop')
+    return utils.format_list_of_dicts(routes)
+
+
+_formatters = {
+    'admin_state_up': _format_admin_state,
+    'external_gateway_info': _format_external_gateway_info,
+    'availability_zones': utils.format_list,
+    'availability_zone_hints': utils.format_list,
+    'routes': _format_routes,
+}
+
+
+def _get_columns(item):
+    columns = list(item.keys())
+    if 'tenant_id' in columns:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    return tuple(sorted(columns))
+
+
+def _get_attrs(client_manager, parsed_args):
+    attrs = {}
+    if parsed_args.name is not None:
+        attrs['name'] = str(parsed_args.name)
+    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
+
+    # "router 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
+
+    # TODO(tangchen): Support getting 'ha' property.
+    # TODO(tangchen): Support getting 'external_gateway_info' property.
+
+    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"""
+
+    def get_parser(self, prog_name):
+        parser = super(CreateRouter, self).get_parser(prog_name)
+        parser.add_argument(
+            'name',
+            metavar='<name>',
+            help=_("New router name")
+        )
+        admin_group = parser.add_mutually_exclusive_group()
+        admin_group.add_argument(
+            '--enable',
+            action='store_true',
+            default=True,
+            help=_("Enable router (default)")
+        )
+        admin_group.add_argument(
+            '--disable',
+            action='store_true',
+            help=_("Disable router")
+        )
+        parser.add_argument(
+            '--distributed',
+            dest='distributed',
+            action='store_true',
+            default=False,
+            help=_("Create a distributed router")
+        )
+        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(
+            '--availability-zone-hint',
+            metavar='<availability-zone>',
+            action='append',
+            dest='availability_zone_hints',
+            help=_("Availability Zone in which to create this router "
+                   "(Router Availability Zone extension required, "
+                   "repeat option to set multiple availability zones)")
+        )
+
+        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_router(**attrs)
+
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+
+        return columns, data
+
+
+class DeleteRouter(command.Command):
+    """Delete router(s)"""
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteRouter, self).get_parser(prog_name)
+        parser.add_argument(
+            'router',
+            metavar="<router>",
+            nargs="+",
+            help=_("Router(s) to delete (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        for router in parsed_args.router:
+            obj = client.find_router(router)
+            client.delete_router(obj)
+
+
+class ListRouter(command.Lister):
+    """List routers"""
+
+    def get_parser(self, prog_name):
+        parser = super(ListRouter, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help=_("List additional fields in output")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+
+        columns = (
+            'id',
+            'name',
+            'status',
+            'admin_state_up',
+            'distributed',
+            'ha',
+            'tenant_id',
+        )
+        column_headers = (
+            'ID',
+            'Name',
+            'Status',
+            'State',
+            'Distributed',
+            'HA',
+            'Project',
+        )
+        if parsed_args.long:
+            columns = columns + (
+                'routes',
+                'external_gateway_info',
+                'availability_zones'
+            )
+            column_headers = column_headers + (
+                'Routes',
+                'External gateway info',
+                'Availability zones'
+            )
+
+        #data = client.routers()
+        data = client.api.router_list()
+
+        return (column_headers,
+                (utils.get_dict_properties(
+                    s, columns,
+                    formatters=_formatters,
+                ) 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"""
+
+    def get_parser(self, prog_name):
+        parser = super(SetRouter, self).get_parser(prog_name)
+        parser.add_argument(
+            'router',
+            metavar="<router>",
+            help=_("Router to modify (name or ID)")
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<name>',
+            help=_("Set router name")
+        )
+        admin_group = parser.add_mutually_exclusive_group()
+        admin_group.add_argument(
+            '--enable',
+            action='store_true',
+            default=None,
+            help=_("Enable router")
+        )
+        admin_group.add_argument(
+            '--disable',
+            action='store_true',
+            help=_("Disable router")
+        )
+        distribute_group = parser.add_mutually_exclusive_group()
+        distribute_group.add_argument(
+            '--distributed',
+            action='store_true',
+            help=_("Set router to distributed mode (disabled router only)")
+        )
+        distribute_group.add_argument(
+            '--centralized',
+            action='store_true',
+            help=_("Set router to centralized mode (disabled router only)")
+        )
+        routes_group = parser.add_mutually_exclusive_group()
+        routes_group.add_argument(
+            '--route',
+            metavar='destination=<subnet>,gateway=<ip-address>',
+            action=parseractions.MultiKeyValueAction,
+            dest='routes',
+            default=None,
+            required_keys=['destination', 'gateway'],
+            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(
+            '--no-route',
+            action='store_true',
+            help=_("Clear routes associated with the router")
+        )
+        routes_group.add_argument(
+            '--clear-routes',
+            action='store_true',
+            help=argparse.SUPPRESS,
+        )
+
+        # TODO(tangchen): Support setting 'ha' property in 'router set'
+        # command. It appears that changing the ha state is supported by
+        # neutron under certain conditions.
+
+        # TODO(tangchen): Support setting 'external_gateway_info' property in
+        # 'router set' command.
+
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_router(parsed_args.router, ignore_missing=False)
+
+        # Get the common attributes.
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
+
+        # Get the route attributes.
+        if parsed_args.no_route:
+            attrs['routes'] = []
+        elif parsed_args.clear_routes:
+            attrs['routes'] = []
+            LOG.warning(_(
+                'The --clear-routes option is deprecated, '
+                'please use --no-route instead.'
+            ))
+        elif parsed_args.routes is not None:
+            # Map the route keys and append to the current routes.
+            # The REST API will handle route validation and duplicates.
+            for route in parsed_args.routes:
+                route['nexthop'] = route.pop('gateway')
+            attrs['routes'] = obj.routes + parsed_args.routes
+
+        if attrs == {}:
+            msg = _("Nothing specified to be set")
+            raise exceptions.CommandError(msg)
+
+        client.update_router(obj, **attrs)
+
+
+class ShowRouter(command.ShowOne):
+    """Display router details"""
+
+    def get_parser(self, prog_name):
+        parser = super(ShowRouter, self).get_parser(prog_name)
+        parser.add_argument(
+            'router',
+            metavar="<router>",
+            help=_("Router to display (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_router(parsed_args.router, ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+        return columns, data
Index: python-openstackclient-1.5.0/openstackclient/network/v2/security_group.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/security_group.py
@@ -0,0 +1,327 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""Security Group action implementations"""
+
+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
+
+
+def _format_network_security_group_rules(sg_rules):
+    # For readability and to align with formatting compute security group
+    # rules, trim keys with caller known (e.g. security group and tenant ID)
+    # or empty values.
+    for sg_rule in sg_rules:
+        empty_keys = [k for k, v in six.iteritems(sg_rule) if not v]
+        for key in empty_keys:
+            sg_rule.pop(key)
+        sg_rule.pop('security_group_id', None)
+        sg_rule.pop('tenant_id', None)
+    return utils.format_list_of_dicts(sg_rules)
+
+
+def _format_compute_security_group_rule(sg_rule):
+    info = network_utils.transform_compute_security_group_rule(sg_rule)
+    # Trim parent security group ID since caller has this information.
+    info.pop('parent_group_id', None)
+    # Trim keys with empty string values.
+    keys_to_trim = [
+        'ip_protocol',
+        'ip_range',
+        'port_range',
+        'remote_security_group',
+    ]
+    for key in keys_to_trim:
+        if key in info and not info[key]:
+            info.pop(key)
+    return utils.format_dict(info)
+
+
+def _format_compute_security_group_rules(sg_rules):
+    rules = []
+    for sg_rule in sg_rules:
+        rules.append(_format_compute_security_group_rule(sg_rule))
+    return utils.format_list(rules, separator='\n')
+
+
+_formatters_network = {
+    'security_group_rules': _format_network_security_group_rules,
+}
+
+
+_formatters_compute = {
+    'rules': _format_compute_security_group_rules,
+}
+
+
+def _get_columns(item):
+    # Build the display columns and a list of the property columns
+    # that need to be mapped (display column name, property name).
+    columns = list(item.keys())
+    property_column_mappings = []
+    if 'security_group_rules' in columns:
+        columns.append('rules')
+        columns.remove('security_group_rules')
+        property_column_mappings.append(('rules', 'security_group_rules'))
+    if 'tenant_id' in columns:
+        columns.append('project_id')
+        columns.remove('tenant_id')
+        property_column_mappings.append(('project_id', 'tenant_id'))
+    display_columns = sorted(columns)
+
+    # Build the property columns and apply any column mappings.
+    property_columns = sorted(columns)
+    for property_column_mapping in property_column_mappings:
+        property_index = property_columns.index(property_column_mapping[0])
+        property_columns[property_index] = property_column_mapping[1]
+    return tuple(display_columns), property_columns
+
+
+class CreateSecurityGroup(common.NetworkAndComputeShowOne):
+    """Create a new security group"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            "name",
+            metavar="<name>",
+            help=_("New security group name")
+        )
+        parser.add_argument(
+            "--description",
+            metavar="<description>",
+            help=_("Security group description")
+        )
+        return parser
+
+    def update_parser_network(self, parser):
+        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 _get_description(self, parsed_args):
+        if parsed_args.description is not None:
+            return parsed_args.description
+        else:
+            return parsed_args.name
+
+    def take_action_network(self, client, parsed_args):
+        # Build the create attributes.
+        attrs = {}
+        attrs['name'] = parsed_args.name
+        attrs['description'] = self._get_description(parsed_args)
+        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 the security group and display the results.
+        obj = client.create_security_group(**attrs)
+        display_columns, property_columns = _get_columns(obj)
+        data = utils.get_item_properties(
+            obj,
+            property_columns,
+            formatters=_formatters_network
+        )
+        return (display_columns, data)
+
+    def take_action_compute(self, client, parsed_args):
+        description = self._get_description(parsed_args)
+        obj = client.security_groups.create(
+            parsed_args.name,
+            description,
+        )
+        display_columns, property_columns = _get_columns(obj._info)
+        data = utils.get_dict_properties(
+            obj._info,
+            property_columns,
+            formatters=_formatters_compute
+        )
+        return (display_columns, data)
+
+
+class DeleteSecurityGroup(common.NetworkAndComputeCommand):
+    """Delete a security group"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'group',
+            metavar='<group>',
+            help=_("Security group to delete (name or ID)")
+        )
+        return parser
+
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_security_group(parsed_args.group)
+        client.delete_security_group(obj)
+
+    def take_action_compute(self, client, parsed_args):
+        data = utils.find_resource(
+            client.security_groups,
+            parsed_args.group,
+        )
+        client.security_groups.delete(data.id)
+
+
+class ListSecurityGroup(common.NetworkAndComputeLister):
+    """List security groups"""
+
+    def update_parser_network(self, parser):
+        # Maintain and hide the argument for backwards compatibility.
+        # Network will always return all projects for an admin.
+        parser.add_argument(
+            '--all-projects',
+            action='store_true',
+            default=False,
+            help=argparse.SUPPRESS,
+        )
+        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)")
+        )
+        return parser
+
+    def _get_return_data(self, data, include_project=True):
+        columns = (
+            "ID",
+            "Name",
+            "Description",
+        )
+        column_headers = columns
+        if include_project:
+            columns = columns + ('Tenant ID',)
+            column_headers = column_headers + ('Project',)
+        return (column_headers,
+                (utils.get_dict_properties(
+                    s, columns,
+                ) for s in data))
+
+    def take_action_network(self, client, parsed_args):
+        return self._get_return_data(client.api.secgroup_list())
+        #return self._get_return_data(client.security_groups())
+
+    def take_action_compute(self, client, parsed_args):
+        search = {'all_tenants': parsed_args.all_projects}
+        data = client.security_groups.list(search_opts=search)
+	#data = client.api.secgroup_list(search_opts=search)
+        return self._get_return_data(data,
+                                     include_project=parsed_args.all_projects)
+
+
+class SetSecurityGroup(common.NetworkAndComputeCommand):
+    """Set security group properties"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'group',
+            metavar='<group>',
+            help=_("Security group to modify (name or ID)")
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<new-name>',
+            help=_("New security group name")
+        )
+        parser.add_argument(
+            "--description",
+            metavar="<description>",
+            help=_("New security group description")
+        )
+        return parser
+
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_security_group(parsed_args.group,
+                                         ignore_missing=False)
+        attrs = {}
+        if parsed_args.name is not None:
+            attrs['name'] = parsed_args.name
+        if parsed_args.description is not None:
+            attrs['description'] = parsed_args.description
+        # NOTE(rtheis): Previous behavior did not raise a CommandError
+        # if there were no updates. Maintain this behavior and issue
+        # the update.
+        client.update_security_group(obj, **attrs)
+
+    def take_action_compute(self, client, parsed_args):
+        data = utils.find_resource(
+            client.security_groups,
+            parsed_args.group,
+        )
+
+        if parsed_args.name is not None:
+            data.name = parsed_args.name
+        if parsed_args.description is not None:
+            data.description = parsed_args.description
+
+        # NOTE(rtheis): Previous behavior did not raise a CommandError
+        # if there were no updates. Maintain this behavior and issue
+        # the update.
+        client.security_groups.update(
+            data,
+            data.name,
+            data.description,
+        )
+
+
+class ShowSecurityGroup(common.NetworkAndComputeShowOne):
+    """Display security group details"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'group',
+            metavar='<group>',
+            help=_("Security group to display (name or ID)")
+        )
+        return parser
+
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_security_group(parsed_args.group,
+                                         ignore_missing=False)
+        display_columns, property_columns = _get_columns(obj)
+        data = utils.get_item_properties(
+            obj,
+            property_columns,
+            formatters=_formatters_network
+        )
+        return (display_columns, data)
+
+    def take_action_compute(self, client, parsed_args):
+        obj = utils.find_resource(
+            client.security_groups,
+            parsed_args.group,
+        )
+        display_columns, property_columns = _get_columns(obj._info)
+        data = utils.get_dict_properties(
+            obj._info,
+            property_columns,
+            formatters=_formatters_compute
+        )
+        return (display_columns, data)
Index: python-openstackclient-1.5.0/openstackclient/network/v2/security_group_rule.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/security_group_rule.py
@@ -0,0 +1,528 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""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
+
+
+def _format_security_group_rule_show(obj):
+    data = network_utils.transform_compute_security_group_rule(obj)
+    return zip(*sorted(six.iteritems(data)))
+
+
+def _format_network_port_range(rule):
+    # Display port range or ICMP type and code. For example:
+    # - ICMP type: 'type=3'
+    # - ICMP type and code: 'type=3:code=0'
+    # - ICMP code: Not supported
+    # - Matching port range: '443:443'
+    # - Different port range: '22:24'
+    # - Single port: '80:80'
+    # - No port range: ''
+    port_range = ''
+    if _is_icmp_protocol(rule.protocol):
+        if rule.port_range_min:
+            port_range += 'type=' + str(rule.port_range_min)
+        if rule.port_range_max:
+            port_range += ':code=' + str(rule.port_range_max)
+    elif rule.port_range_min or rule.port_range_max:
+        port_range_min = str(rule.port_range_min)
+        port_range_max = str(rule.port_range_max)
+        if rule.port_range_min is None:
+            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:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    return tuple(sorted(columns))
+
+
+def _convert_to_lowercase(string):
+    return string.lower()
+
+
+def _is_icmp_protocol(protocol):
+    # NOTE(rtheis): Neutron has deprecated protocol icmpv6.
+    # However, while the OSC CLI doesn't document the protocol,
+    # the code must still handle it. In addition, handle both
+    # protocol names and numbers.
+    if protocol in ['icmp', 'icmpv6', 'ipv6-icmp', '1', '58']:
+        return True
+    else:
+        return False
+
+
+class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
+    """Create a new security group rule"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'group',
+            metavar='<group>',
+            help=_("Create rule in this security group (name or ID)")
+        )
+        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)")
+        )
+        return parser
+
+    def update_parser_network(self, parser):
+        parser.add_argument(
+            '--dst-port',
+            metavar='<port-range>',
+            action=parseractions.RangeAction,
+            help=_("Destination port, may be a single port or a starting and "
+                   "ending port range: 137:139. Required for IP protocols TCP "
+                   "and UDP. Ignored for ICMP IP protocols.")
+        )
+        parser.add_argument(
+            '--icmp-type',
+            metavar='<icmp-type>',
+            type=int,
+            help=_("ICMP type for ICMP IP protocols")
+        )
+        parser.add_argument(
+            '--icmp-code',
+            metavar='<icmp-code>',
+            type=int,
+            help=_("ICMP code for ICMP IP protocols")
+        )
+        # NOTE(rtheis): Support either protocol option name for now.
+        # However, consider deprecating and then removing --proto in
+        # a future release.
+        protocol_group = parser.add_mutually_exclusive_group()
+        protocol_group.add_argument(
+            '--protocol',
+            metavar='<protocol>',
+            type=_convert_to_lowercase,
+            help=_("IP protocol (ah, dccp, egp, esp, gre, icmp, igmp, "
+                   "ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, "
+                   "ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, "
+                   "udp, udplite, vrrp and integer representations [0-255]; "
+                   "default: tcp)")
+        )
+        protocol_group.add_argument(
+            '--proto',
+            metavar='<proto>',
+            type=_convert_to_lowercase,
+            help=argparse.SUPPRESS
+        )
+        direction_group = parser.add_mutually_exclusive_group()
+        direction_group.add_argument(
+            '--ingress',
+            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: based on IP protocol)")
+        )
+        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 update_parser_compute(self, parser):
+        parser.add_argument(
+            '--dst-port',
+            metavar='<port-range>',
+            default=(0, 0),
+            action=parseractions.RangeAction,
+            help=_("Destination port, may be a single port or a starting and "
+                   "ending port range: 137:139. Required for IP protocols TCP "
+                   "and UDP. Ignored for ICMP IP protocols.")
+        )
+        # NOTE(rtheis): Support either protocol option name for now.
+        # However, consider deprecating and then removing --proto in
+        # a future release.
+        protocol_group = parser.add_mutually_exclusive_group()
+        protocol_group.add_argument(
+            '--protocol',
+            metavar='<protocol>',
+            choices=['icmp', 'tcp', 'udp'],
+            type=_convert_to_lowercase,
+            help=_("IP protocol (icmp, tcp, udp; default: tcp)")
+        )
+        protocol_group.add_argument(
+            '--proto',
+            metavar='<proto>',
+            choices=['icmp', 'tcp', 'udp'],
+            type=_convert_to_lowercase,
+            help=argparse.SUPPRESS
+        )
+        return parser
+
+    def _get_protocol(self, parsed_args):
+        protocol = 'tcp'
+        if parsed_args.protocol is not None:
+            protocol = parsed_args.protocol
+        if parsed_args.proto is not None:
+            protocol = parsed_args.proto
+        return protocol
+
+    def _is_ipv6_protocol(self, protocol):
+        # NOTE(rtheis): Neutron has deprecated protocol icmpv6.
+        # However, while the OSC CLI doesn't document the protocol,
+        # the code must still handle it. In addition, handle both
+        # protocol names and numbers.
+        if (protocol.startswith('ipv6-') or
+                protocol in ['icmpv6', '41', '43', '44', '58', '59', '60']):
+            return True
+        else:
+            return False
+
+    def take_action_network(self, client, parsed_args):
+        # Get the security group ID to hold the rule.
+        security_group_id = client.find_security_group(
+            parsed_args.group,
+            ignore_missing=False
+        ).id
+
+        # Build the create attributes.
+        attrs = {}
+        attrs['protocol'] = self._get_protocol(parsed_args)
+
+        # NOTE(rtheis): A direction must be specified and ingress
+        # is the default.
+        if parsed_args.ingress or not parsed_args.egress:
+            attrs['direction'] = 'ingress'
+        if parsed_args.egress:
+            attrs['direction'] = 'egress'
+
+        # NOTE(rtheis): Use ethertype specified else default based
+        # on IP protocol.
+        if parsed_args.ethertype:
+            attrs['ethertype'] = parsed_args.ethertype
+        elif self._is_ipv6_protocol(attrs['protocol']):
+            attrs['ethertype'] = 'IPv6'
+        else:
+            attrs['ethertype'] = 'IPv4'
+
+        # NOTE(rtheis): Validate the port range and ICMP type and code.
+        # It would be ideal if argparse could do this.
+        if parsed_args.dst_port and (parsed_args.icmp_type or
+                                     parsed_args.icmp_code):
+            msg = _('Argument --dst-port not allowed with arguments '
+                    '--icmp-type and --icmp-code')
+            raise exceptions.CommandError(msg)
+        if parsed_args.icmp_type is None and parsed_args.icmp_code is not None:
+            msg = _('Argument --icmp-type required with argument --icmp-code')
+            raise exceptions.CommandError(msg)
+        is_icmp_protocol = _is_icmp_protocol(attrs['protocol'])
+        if not is_icmp_protocol and (parsed_args.icmp_type or
+                                     parsed_args.icmp_code):
+            msg = _('ICMP IP protocol required with arguments '
+                    '--icmp-type and --icmp-code')
+            raise exceptions.CommandError(msg)
+        # NOTE(rtheis): For backwards compatibility, continue ignoring
+        # the destination port range when an ICMP IP protocol is specified.
+        if parsed_args.dst_port and not is_icmp_protocol:
+            attrs['port_range_min'] = parsed_args.dst_port[0]
+            attrs['port_range_max'] = parsed_args.dst_port[1]
+        if parsed_args.icmp_type:
+            attrs['port_range_min'] = parsed_args.icmp_type
+        if parsed_args.icmp_code:
+            attrs['port_range_max'] = parsed_args.icmp_code
+
+        if parsed_args.src_group is not None:
+            attrs['remote_group_id'] = client.find_security_group(
+                parsed_args.src_group,
+                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,
+        )
+        protocol = self._get_protocol(parsed_args)
+        if protocol == '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,
+            protocol,
+            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"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'rule',
+            metavar='<rule>',
+            help=_("Security group rule to delete (ID only)")
+        )
+        return parser
+
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_security_group_rule(parsed_args.rule)
+        client.delete_security_group_rule(obj)
+
+    def take_action_compute(self, client, parsed_args):
+        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))
+	rules = client.api.secgrouprule_list(**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"""
+
+    def update_parser_common(self, parser):
+        parser.add_argument(
+            'rule',
+            metavar="<rule>",
+            help=_("Security group rule to display (ID only)")
+        )
+        return parser
+
+    def take_action_network(self, client, parsed_args):
+        obj = client.find_security_group_rule(parsed_args.rule,
+                                              ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns)
+        return (columns, data)
+
+    def take_action_compute(self, client, parsed_args):
+        # NOTE(rtheis): Unfortunately, compute does not have an API
+        # to get or list security group rules so parse through the
+        # security groups to find all accessible rules in search of
+        # the requested rule.
+        obj = None
+        security_group_rules = []
+        for security_group in client.security_groups.list():
+            security_group_rules.extend(security_group.rules)
+        for security_group_rule in security_group_rules:
+            if parsed_args.rule == str(security_group_rule.get('id')):
+                obj = security_group_rule
+                break
+
+        if obj is None:
+            msg = _("Could not find security group rule with ID ") + \
+                parsed_args.rule
+            raise exceptions.CommandError(msg)
+
+        # NOTE(rtheis): Format security group rule
+        return _format_security_group_rule_show(obj)
Index: python-openstackclient-1.5.0/openstackclient/network/v2/subnet_pool.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/subnet_pool.py
@@ -0,0 +1,321 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""Subnet pool action implementations"""
+
+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):
+    columns = list(item.keys())
+    if 'tenant_id' in columns:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    return tuple(sorted(columns))
+
+
+_formatters = {
+    'prefixes': utils.format_list,
+}
+
+
+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_prefixlen'] = parsed_args.default_prefix_length
+    if parsed_args.min_prefix_length is not None:
+        attrs['min_prefixlen'] = parsed_args.min_prefix_length
+    if parsed_args.max_prefix_length is not None:
+        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
+
+    if parsed_args.default:
+        attrs['is_default'] = True
+    if parsed_args.no_default:
+        attrs['is_default'] = False
+
+    if 'share' in parsed_args and parsed_args.share:
+        attrs['shared'] = True
+    if 'no_share' in parsed_args and parsed_args.no_share:
+        attrs['shared'] = False
+
+    # "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
+
+
+def _add_prefix_options(parser, for_create=False):
+    parser.add_argument(
+        '--pool-prefix',
+        metavar='<pool-prefix>',
+        dest='prefixes',
+        action='append',
+        required=for_create,
+        help=_("Set subnet pool prefixes (in CIDR notation) "
+               "(repeat option to set multiple prefixes)")
+    )
+    parser.add_argument(
+        '--default-prefix-length',
+        metavar='<default-prefix-length>',
+        type=int,
+        action=parseractions.NonNegativeAction,
+        help=_("Set subnet pool default prefix length")
+    )
+    parser.add_argument(
+        '--min-prefix-length',
+        metavar='<min-prefix-length>',
+        action=parseractions.NonNegativeAction,
+        type=int,
+        help=_("Set subnet pool minimum prefix length")
+    )
+    parser.add_argument(
+        '--max-prefix-length',
+        metavar='<max-prefix-length>',
+        type=int,
+        action=parseractions.NonNegativeAction,
+        help=_("Set subnet pool maximum prefix length")
+    )
+
+
+def _add_default_options(parser):
+    default_group = parser.add_mutually_exclusive_group()
+    default_group.add_argument(
+        '--default',
+        action='store_true',
+        help=_("Set this as a default subnet pool"),
+    )
+    default_group.add_argument(
+        '--no-default',
+        action='store_true',
+        help=_("Set this as a non-default subnet pool"),
+    )
+
+
+class CreateSubnetPool(command.ShowOne):
+    """Create subnet pool"""
+
+    def get_parser(self, prog_name):
+        parser = super(CreateSubnetPool, self).get_parser(prog_name)
+        parser.add_argument(
+            'name',
+            metavar='<name>',
+            help=_("Name of the new subnet pool")
+        )
+        _add_prefix_options(parser, for_create=True)
+        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")
+        )
+        _add_default_options(parser)
+        shared_group = parser.add_mutually_exclusive_group()
+        shared_group.add_argument(
+            '--share',
+            action='store_true',
+            help=_("Set this subnet pool as shared"),
+        )
+        shared_group.add_argument(
+            '--no-share',
+            action='store_true',
+            help=_("Set this subnet pool as not shared"),
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        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)
+        return (columns, data)
+
+
+class DeleteSubnetPool(command.Command):
+    """Delete subnet pool"""
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteSubnetPool, self).get_parser(prog_name)
+        parser.add_argument(
+            'subnet_pool',
+            metavar='<subnet-pool>',
+            help=_("Subnet pool to delete (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_subnet_pool(parsed_args.subnet_pool)
+        client.delete_subnet_pool(obj)
+
+
+class ListSubnetPool(command.Lister):
+    """List subnet pools"""
+
+    def get_parser(self, prog_name):
+        parser = super(ListSubnetPool, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help=_("List additional fields in output")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        #data = self.app.client_manager.network.subnet_pools()
+	data = self.app.client_manager.network.api.subpool_list()
+
+        if parsed_args.long:
+            headers = (
+                'ID',
+                'Name',
+                'Prefixes',
+                'Default Prefix Length',
+                'Address Scope',
+                'Default Subnet Pool',
+                'Shared',
+            )
+            columns = (
+                'id',
+                'name',
+                'prefixes',
+                'default_prefixlen',
+                'address_scope_id',
+                'is_default',
+                'shared',
+            )
+        else:
+            headers = (
+                'ID',
+                'Name',
+                'Prefixes',
+            )
+            columns = (
+                'id',
+                'name',
+                'prefixes',
+            )
+
+        return (headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters=_formatters,
+                ) for s in data))
+
+
+class SetSubnetPool(command.Command):
+    """Set subnet pool properties"""
+
+    def get_parser(self, prog_name):
+        parser = super(SetSubnetPool, self).get_parser(prog_name)
+        parser.add_argument(
+            'subnet_pool',
+            metavar='<subnet-pool>',
+            help=_("Subnet pool to modify (name or ID)")
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<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")
+        )
+        _add_default_options(parser)
+
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_subnet_pool(parsed_args.subnet_pool,
+                                      ignore_missing=False)
+
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
+        if attrs == {}:
+            msg = _("Nothing specified to be set")
+            raise exceptions.CommandError(msg)
+
+        # Existing prefixes must be a subset of the new prefixes.
+        if 'prefixes' in attrs:
+            attrs['prefixes'].extend(obj.prefixes)
+
+        client.update_subnet_pool(obj, **attrs)
+
+
+class ShowSubnetPool(command.ShowOne):
+    """Display subnet pool details"""
+
+    def get_parser(self, prog_name):
+        parser = super(ShowSubnetPool, self).get_parser(prog_name)
+        parser.add_argument(
+            'subnet_pool',
+            metavar='<subnet-pool>',
+            help=_("Subnet pool to display (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        obj = client.find_subnet_pool(
+            parsed_args.subnet_pool,
+            ignore_missing=False
+        )
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+        return (columns, data)
Index: python-openstackclient-1.5.0/openstackclient/network/v2/subnet.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/network/v2/subnet.py
@@ -0,0 +1,408 @@
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+#
+
+"""Subnet action implementations"""
+import copy
+
+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 _format_allocation_pools(data):
+    pool_formatted = ['%s-%s' % (pool.get('start', ''), pool.get('end', ''))
+                      for pool in data]
+    return ','.join(pool_formatted)
+
+
+def _format_host_routes(data):
+    # Map the host route keys to match --host-route option.
+    return utils.format_list_of_dicts(convert_entries_to_gateway(data))
+
+
+_formatters = {
+    'allocation_pools': _format_allocation_pools,
+    'dns_nameservers': utils.format_list,
+    'host_routes': _format_host_routes,
+}
+
+
+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:
+        columns.remove('tenant_id')
+        columns.append('project_id')
+    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:
+        if 'gateway' in entry:
+            entry['nexthop'] = entry['gateway']
+            del entry['gateway']
+
+    return changed_entries
+
+
+def convert_entries_to_gateway(entries):
+    # Change 'nexthop' entry to 'gateway'
+    changed_entries = copy.deepcopy(entries)
+    for entry in changed_entries:
+        if 'nexthop' in entry:
+            entry['gateway'] = entry['nexthop']
+            del entry['nexthop']
+
+    return changed_entries
+
+
+def _get_attrs(client_manager, parsed_args, is_create=True):
+    attrs = {}
+    if 'name' in parsed_args and parsed_args.name is not None:
+        attrs['name'] = str(parsed_args.name)
+
+    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':
+            msg = _("Auto option is not available for Subnet Set. "
+                    "Valid options are <ip-address> or none")
+            raise exceptions.CommandError(msg)
+        elif gateway != 'auto':
+            if gateway == 'none':
+                attrs['gateway_ip'] = None
+            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.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 '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
+
+
+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 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)")
+        )
+        dhcp_enable_group = parser.add_mutually_exclusive_group()
+        dhcp_enable_group.add_argument(
+            '--dhcp',
+            action='store_true',
+            default=True,
+            help=_("Enable DHCP (default)")
+        )
+        dhcp_enable_group.add_argument(
+            '--no-dhcp',
+            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(
+            '--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)")
+        )
+        _get_common_parse_arguments(parser)
+        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):
+    """Delete subnet"""
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteSubnet, self).get_parser(prog_name)
+        parser.add_argument(
+            'subnet',
+            metavar="<subnet>",
+            help=_("Subnet to delete (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        client.delete_subnet(
+            client.find_subnet(parsed_args.subnet))
+
+
+class ListSubnet(command.Lister):
+    """List subnets"""
+
+    def get_parser(self, prog_name):
+        parser = super(ListSubnet, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help=_("List additional fields in output")
+        )
+        parser.add_argument(
+            '--ip-version',
+            type=int,
+            choices=[4, 6],
+            metavar='<ip-version>',
+            dest='ip_version',
+            help=_("List only subnets of given IP version in output"
+                   "Allowed values for IP version are 4 and 6."),
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        filters = {}
+        if parsed_args.ip_version:
+            filters['ip_version'] = parsed_args.ip_version
+        #data = self.app.client_manager.network.subnets(**filters)
+        client = self.app.client_manager.network
+        data = client.api.subnet_list(**filters)
+
+        headers = ('ID', 'Name', 'Network', 'Subnet')
+        columns = ('id', 'name', 'network_id', 'cidr')
+        if parsed_args.long:
+            headers += ('Project', 'DHCP', 'Name Servers',
+                        'Allocation Pools', 'Host Routes', 'IP Version',
+                        'Gateway')
+            columns += ('tenant_id', 'enable_dhcp', 'dns_nameservers',
+                        'allocation_pools', 'host_routes', 'ip_version',
+                        'gateway_ip')
+
+        return (headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters=_formatters,
+                ) 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):
+    """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 display (name or ID)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        obj = self.app.client_manager.network.find_subnet(parsed_args.subnet,
+                                                          ignore_missing=False)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+        return (columns, data)
Index: python-openstackclient-1.5.0/setup.cfg
===================================================================
--- python-openstackclient-1.5.0.orig/setup.cfg
+++ python-openstackclient-1.5.0/setup.cfg
@@ -74,11 +74,8 @@ openstack.compute.v2 =
 	hypervisor_stats_show = openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats
 	ip_fixed_add = openstackclient.compute.v2.fixedip:AddFixedIP
 	ip_fixed_remove = openstackclient.compute.v2.fixedip:RemoveFixedIP
 	ip_floating_add = openstackclient.compute.v2.floatingip:AddFloatingIP
-	ip_floating_create = openstackclient.compute.v2.floatingip:CreateFloatingIP
-	ip_floating_delete = openstackclient.compute.v2.floatingip:DeleteFloatingIP
-	ip_floating_list = openstackclient.compute.v2.floatingip:ListFloatingIP
 	ip_floating_remove = openstackclient.compute.v2.floatingip:RemoveFloatingIP
 	ip_floating_pool_list = openstackclient.compute.v2.floatingippool:ListFloatingIPPool
 	keypair_create = openstackclient.compute.v2.keypair:CreateKeypair
 	keypair_delete = openstackclient.compute.v2.keypair:DeleteKeypair
@@ -265,13 +262,59 @@ openstack.image.v2 =
 	image_save = openstackclient.image.v2.image:SaveImage
 	image_show = openstackclient.image.v2.image:ShowImage
 	image_set = openstackclient.image.v2.image:SetImage
 openstack.network.v2 = 
+	address_scope_create = openstackclient.network.v2.address_scope:CreateAddressScope
+	address_scope_delete = openstackclient.network.v2.address_scope:DeleteAddressScope
+	address_scope_list = openstackclient.network.v2.address_scope:ListAddressScope
+	address_scope_set = openstackclient.network.v2.address_scope:SetAddressScope
+	address_scope_show = openstackclient.network.v2.address_scope:ShowAddressScope
+	ip_availability_list = openstackclient.network.v2.ip_availability:ListIPAvailability
+	ip_availability_show = openstackclient.network.v2.ip_availability:ShowIPAvailability
+	ip_floating_create = openstackclient.network.v2.floating_ip:CreateFloatingIP
+	ip_floating_delete = openstackclient.network.v2.floating_ip:DeleteFloatingIP
+	ip_floating_list = openstackclient.network.v2.floating_ip:ListFloatingIP
+	ip_floating_show = openstackclient.network.v2.floating_ip:ShowFloatingIP
 	network_create = openstackclient.network.v2.network:CreateNetwork
 	network_delete = openstackclient.network.v2.network:DeleteNetwork
 	network_list = openstackclient.network.v2.network:ListNetwork
 	network_set = openstackclient.network.v2.network:SetNetwork
 	network_show = openstackclient.network.v2.network:ShowNetwork
+	network_segment_list = openstackclient.network.v2.network_segment:ListNetworkSegment
+	network_segment_show = openstackclient.network.v2.network_segment:ShowNetworkSegment
+	port_create = openstackclient.network.v2.port:CreatePort
+	port_delete = openstackclient.network.v2.port:DeletePort
+	port_list = openstackclient.network.v2.port:ListPort
+	port_set = openstackclient.network.v2.port:SetPort
+	port_show = openstackclient.network.v2.port:ShowPort
+	router_add_port = openstackclient.network.v2.router:AddPortToRouter
+	router_add_subnet = openstackclient.network.v2.router:AddSubnetToRouter
+	router_create = openstackclient.network.v2.router:CreateRouter
+	router_delete = openstackclient.network.v2.router:DeleteRouter
+	router_list = openstackclient.network.v2.router:ListRouter
+	router_remove_port = openstackclient.network.v2.router:RemovePortFromRouter
+	router_remove_subnet = openstackclient.network.v2.router:RemoveSubnetFromRouter
+	router_set = openstackclient.network.v2.router:SetRouter
+	router_show = openstackclient.network.v2.router:ShowRouter
+	security2_group_create = openstackclient.network.v2.security_group:CreateSecurityGroup
+	security2_group_delete = openstackclient.network.v2.security_group:DeleteSecurityGroup
+	security2_group_list = openstackclient.network.v2.security_group:ListSecurityGroup
+	security2_group_set = openstackclient.network.v2.security_group:SetSecurityGroup
+	security2_group_show = openstackclient.network.v2.security_group:ShowSecurityGroup
+	security2_group_rule_create = openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule
+	security2_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule
+	security2_group_rule_list = openstackclient.network.v2.security_group_rule:ListSecurityGroupRule
+	security2_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_list = openstackclient.network.v2.subnet:ListSubnet
+	subnet_set = openstackclient.network.v2.subnet:SetSubnet
+	subnet_show = openstackclient.network.v2.subnet:ShowSubnet
+	subnet_pool_create = openstackclient.network.v2.subnet_pool:CreateSubnetPool
+	subnet_pool_delete = openstackclient.network.v2.subnet_pool:DeleteSubnetPool
+	subnet_pool_list = openstackclient.network.v2.subnet_pool:ListSubnetPool
+	subnet_pool_set = openstackclient.network.v2.subnet_pool:SetSubnetPool
+	subnet_pool_show = openstackclient.network.v2.subnet_pool:ShowSubnetPool
 openstack.object_store.v1 = 
 	container_create = openstackclient.object.v1.container:CreateContainer
 	container_delete = openstackclient.object.v1.container:DeleteContainer
 	container_list = openstackclient.object.v1.container:ListContainer
Index: python-openstackclient-1.5.0/python_openstackclient.egg-info/SOURCES.txt
===================================================================
--- python-openstackclient-1.5.0.orig/python_openstackclient.egg-info/SOURCES.txt
+++ python-openstackclient-1.5.0/python_openstackclient.egg-info/SOURCES.txt
@@ -187,10 +187,21 @@ openstackclient/image/v2/__init__.py
 openstackclient/image/v2/image.py
 openstackclient/network/__init__.py
 openstackclient/network/client.py
 openstackclient/network/common.py
+openstackclient/network/utils.py
 openstackclient/network/v2/__init__.py
+openstackclient/network/v2/address_scope.py
+openstackclient/network/v2/floating_ip.py
+openstackclient/network/v2/ip_availability.py
 openstackclient/network/v2/network.py
+openstackclient/network/v2/network_segment.py
+openstackclient/network/v2/port.py
+openstackclient/network/v2/router.py
+openstackclient/network/v2/security_group.py
+openstackclient/network/v2/security_group_rule.py
+openstackclient/network/v2/subnet.py
+openstackclient/network/v2/subnet_pool.py
 openstackclient/object/__init__.py
 openstackclient/object/client.py
 openstackclient/object/v1/__init__.py
 openstackclient/object/v1/container.py
Index: python-openstackclient-1.5.0/python_openstackclient.egg-info/entry_points.txt
===================================================================
--- python-openstackclient-1.5.0.orig/python_openstackclient.egg-info/entry_points.txt
+++ python-openstackclient-1.5.0/python_openstackclient.egg-info/entry_points.txt
@@ -53,11 +53,8 @@ hypervisor_show = openstackclient.comput
 hypervisor_stats_show = openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats
 ip_fixed_add = openstackclient.compute.v2.fixedip:AddFixedIP
 ip_fixed_remove = openstackclient.compute.v2.fixedip:RemoveFixedIP
 ip_floating_add = openstackclient.compute.v2.floatingip:AddFloatingIP
-ip_floating_create = openstackclient.compute.v2.floatingip:CreateFloatingIP
-ip_floating_delete = openstackclient.compute.v2.floatingip:DeleteFloatingIP
-ip_floating_list = openstackclient.compute.v2.floatingip:ListFloatingIP
 ip_floating_pool_list = openstackclient.compute.v2.floatingippool:ListFloatingIPPool
 ip_floating_remove = openstackclient.compute.v2.floatingip:RemoveFloatingIP
 keypair_create = openstackclient.compute.v2.keypair:CreateKeypair
 keypair_delete = openstackclient.compute.v2.keypair:DeleteKeypair
@@ -249,13 +246,59 @@ image_save = openstackclient.image.v2.im
 image_set = openstackclient.image.v2.image:SetImage
 image_show = openstackclient.image.v2.image:ShowImage
 
 [openstack.network.v2]
+address_scope_create = openstackclient.network.v2.address_scope:CreateAddressScope
+address_scope_delete = openstackclient.network.v2.address_scope:DeleteAddressScope
+address_scope_list = openstackclient.network.v2.address_scope:ListAddressScope
+address_scope_set = openstackclient.network.v2.address_scope:SetAddressScope
+address_scope_show = openstackclient.network.v2.address_scope:ShowAddressScope
+ip_availability_list = openstackclient.network.v2.ip_availability:ListIPAvailability
+ip_availability_show = openstackclient.network.v2.ip_availability:ShowIPAvailability
+ip_floating_create = openstackclient.network.v2.floating_ip:CreateFloatingIP
+ip_floating_delete = openstackclient.network.v2.floating_ip:DeleteFloatingIP
+ip_floating_list = openstackclient.network.v2.floating_ip:ListFloatingIP
+ip_floating_show = openstackclient.network.v2.floating_ip:ShowFloatingIP
 network_create = openstackclient.network.v2.network:CreateNetwork
 network_delete = openstackclient.network.v2.network:DeleteNetwork
 network_list = openstackclient.network.v2.network:ListNetwork
+network_segment_list = openstackclient.network.v2.network_segment:ListNetworkSegment
+network_segment_show = openstackclient.network.v2.network_segment:ShowNetworkSegment
 network_set = openstackclient.network.v2.network:SetNetwork
 network_show = openstackclient.network.v2.network:ShowNetwork
+port_create = openstackclient.network.v2.port:CreatePort
+port_delete = openstackclient.network.v2.port:DeletePort
+port_list = openstackclient.network.v2.port:ListPort
+port_set = openstackclient.network.v2.port:SetPort
+port_show = openstackclient.network.v2.port:ShowPort
+router_add_port = openstackclient.network.v2.router:AddPortToRouter
+router_add_subnet = openstackclient.network.v2.router:AddSubnetToRouter
+router_create = openstackclient.network.v2.router:CreateRouter
+router_delete = openstackclient.network.v2.router:DeleteRouter
+router_list = openstackclient.network.v2.router:ListRouter
+router_remove_port = openstackclient.network.v2.router:RemovePortFromRouter
+router_remove_subnet = openstackclient.network.v2.router:RemoveSubnetFromRouter
+router_set = openstackclient.network.v2.router:SetRouter
+router_show = openstackclient.network.v2.router:ShowRouter
+security_group_create = openstackclient.network.v2.security_group:CreateSecurityGroup
+security_group_delete = openstackclient.network.v2.security_group:DeleteSecurityGroup
+security_group_list = openstackclient.network.v2.security_group:ListSecurityGroup
+security_group_rule_create = openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule
+security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule
+security_group_rule_list = openstackclient.network.v2.security_group_rule:ListSecurityGroupRule
+security_group_rule_show = openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule
+security_group_set = openstackclient.network.v2.security_group:SetSecurityGroup
+security_group_show = openstackclient.network.v2.security_group:ShowSecurityGroup
+subnet_create = openstackclient.network.v2.subnet:CreateSubnet
+subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet
+subnet_list = openstackclient.network.v2.subnet:ListSubnet
+subnet_pool_create = openstackclient.network.v2.subnet_pool:CreateSubnetPool
+subnet_pool_delete = openstackclient.network.v2.subnet_pool:DeleteSubnetPool
+subnet_pool_list = openstackclient.network.v2.subnet_pool:ListSubnetPool
+subnet_pool_set = openstackclient.network.v2.subnet_pool:SetSubnetPool
+subnet_pool_show = openstackclient.network.v2.subnet_pool:ShowSubnetPool
+subnet_set = openstackclient.network.v2.subnet:SetSubnet
+subnet_show = openstackclient.network.v2.subnet:ShowSubnet
 
 [openstack.object_store.v1]
 container_create = openstackclient.object.v1.container:CreateContainer
 container_delete = openstackclient.object.v1.container:DeleteContainer
Index: python-openstackclient-1.5.0/openstackclient/api/network_v2.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/api/network_v2.py
+++ python-openstackclient-1.5.0/openstackclient/api/network_v2.py
@@ -56,4 +56,125 @@ class APIv2(api.BaseAPI):
 
         if external:
             filter = {'router:external': True}
         return self.list('networks', **filter)['networks']
+
+    def subnet_list(
+        self,
+        **filter
+    ):
+        """List subnets
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('subnets', **filter)['subnets']
+
+
+    def router_list(
+        self,
+        **filter
+    ):
+        """List subnets
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('routers', **filter)['routers']
+
+
+    def fip_list(
+        self,
+        **filter
+    ):
+        """List floating IPs
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('floatingips', **filter)['floatingips']
+
+    def port_list(
+        self,
+        **filter
+    ):
+        """List ports
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('ports', **filter)['ports']
+
+    def segment_list(
+        self,
+        **filter
+    ):
+        """List Network Segments
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('segments', **filter)['segments']
+
+    def avail_list(
+        self,
+        **filter
+    ):
+        """List available IPs
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('network-ip-availabilities', **filter)['network-ip-availabilities']
+
+    def secgroup_list(
+        self,
+        **filter
+    ):
+        """List security groups
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('security-groups', **filter)['security_groups']
+
+    def secgrouprule_list(
+        self,
+        **filter
+    ):
+        """List security group rules
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('security-group-rules', **filter)['security_group_rules']
+
+    def subpool_list(
+        self,
+        **filter
+    ):
+        """List subnet pools
+
+        :param filter:
+            used to create the query string filters
+            http://docs.openstack.org/api/openstack-network/2.0/content/filtering.html
+        """
+
+        return self.list('subnetpools', **filter)['subnetpools']
+
+
Index: python-openstackclient-1.5.0/openstackclient/common/clientmanager.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/common/clientmanager.py
+++ python-openstackclient-1.5.0/openstackclient/common/clientmanager.py
@@ -182,8 +182,28 @@ class ClientManager(object):
             LOG.debug("Get auth_ref")
             self._auth_ref = self.auth.get_auth_ref(self.session)
         return self._auth_ref
 
+    def is_network_endpoint_enabled(self):
+        """Check if the network endpoint is enabled"""
+        # Trigger authentication necessary to determine if the network
+        # endpoint is enabled.
+        if self.auth_ref:
+            service_catalog = self.auth_ref.service_catalog
+        else:
+            service_catalog = None
+        # Assume that the network endpoint is enabled.
+        network_endpoint_enabled = True
+        if service_catalog:
+            if 'network' in service_catalog.get_endpoints():
+                LOG.debug("Network endpoint in service catalog")
+            else:
+                LOG.debug("No network endpoint in service catalog")
+                network_endpoint_enabled = False
+        else:
+            LOG.debug("No service catalog, assuming network endpoint enabled")
+        return network_endpoint_enabled
+
     def get_endpoint_for_service_type(self, service_type, region_name=None):
         """Return the endpoint URL for the service type."""
         # See if we are using password flow auth, i.e. we have a
         # service catalog to select endpoints from
Index: python-openstackclient-1.5.0/openstackclient/common/utils.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/common/utils.py
+++ python-openstackclient-1.5.0/openstackclient/common/utils.py
@@ -164,8 +164,18 @@ def format_list(data):
 
     return ', '.join(sorted(data))
 
 
+def format_list_of_dicts(data):
+    """Return a formatted string of key value pairs for each dict
+
+    :param data: a list of dicts
+    :rtype: a string formatted to key='value' with dicts separated by new line
+    """
+
+    return '\n'.join(format_dict(i) for i in data)
+
+
 def get_field(item, field):
     try:
         if isinstance(item, dict):
             return item[field]
Index: python-openstackclient-1.5.0/openstackclient/identity/common.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/identity/common.py
+++ python-openstackclient-1.5.0/openstackclient/identity/common.py
@@ -108,4 +108,44 @@ def _find_identity_resource(identity_cli
     except identity_exc.Forbidden:
         pass
 
     return resource_type(None, {'id': name_or_id, 'name': name_or_id})
+
+
+def add_user_domain_option_to_parser(parser):
+    parser.add_argument(
+        '--user-domain',
+        metavar='<user-domain>',
+        help=('Domain the user belongs to (name or ID). '
+              'This can be used in case collisions between user names '
+              'exist.')
+    )
+
+
+def add_group_domain_option_to_parser(parser):
+    parser.add_argument(
+        '--group-domain',
+        metavar='<group-domain>',
+        help=('Domain the group belongs to (name or ID). '
+              'This can be used in case collisions between group names '
+              'exist.')
+    )
+
+
+def add_project_domain_option_to_parser(parser):
+    parser.add_argument(
+        '--project-domain',
+        metavar='<project-domain>',
+        help=('Domain the project belongs to (name or ID). '
+              'This can be used in case collisions between project names '
+              'exist.')
+    )
+
+
+def add_inherited_option_to_parser(parser):
+    parser.add_argument(
+        '--inherited',
+        action='store_true',
+        default=False,
+        help=('Specifies if the role grant is inheritable to the sub projects')
+    )
+