From d44782bc089d6f3092cc2ee22ee4a13ea0c40ce9 Mon Sep 17 00:00:00 2001
From: Cao Xuan Hoang <hoangcx@vn.fujitsu.com>
Date: Fri, 16 Sep 2016 14:31:26 +0700
Subject: [PATCH] Add filtering options to os subnet list command

This patch adds the following filtering options:
'--project' and '--project-domain', '--network', '--gateway',
'--name', '--subnet-range' to the command.

Change-Id: I575739486b9548492bd00f50130181b825534226
Partially-Implements: blueprint network-commands-options
Closes-Bug: #1610883
---
 doc/source/command-objects/subnet.rst         |  31 +++++
 openstackclient/network/v2/subnet.py          |  51 +++++++-
 .../tests/unit/network/v2/test_subnet.py      | 110 +++++++++++++++++-
 .../notes/bug-1610883-38929f6fc2eefc9a.yaml   |   8 ++
 4 files changed, 198 insertions(+), 2 deletions(-)
 create mode 100644 releasenotes/notes/bug-1610883-38929f6fc2eefc9a.yaml

diff --git a/doc/source/command-objects/subnet.rst b/doc/source/command-objects/subnet.rst
index 4566d74d7b..d2ea6132c5 100644
--- a/doc/source/command-objects/subnet.rst
+++ b/doc/source/command-objects/subnet.rst
@@ -161,6 +161,11 @@ List subnets
         [--long]
         [--ip-version {4,6}]
         [--dhcp | --no-dhcp]
+        [--project <project> [--project-domain <project-domain>]]
+        [--network <network>]
+        [--gateway <gateway>]
+        [--name <name>]
+        [--subnet-range <subnet-range>]
 
 .. option:: --long
 
@@ -186,6 +191,32 @@ List subnets
     Must be a valid device owner value for a network port
     (repeat option to list multiple service types)
 
+.. option:: --project <project>
+
+    List only subnets which belong to a given project (name or ID) in output
+
+.. option:: --project-domain <project-domain>
+
+    Domain the project belongs to (name or ID).
+    This can be used in case collisions between project names exist.
+
+.. option:: --network <network>
+
+    List only subnets which belong to a given network (name or ID) in output
+
+.. option:: --gateway <gateway>
+
+    List only subnets of given gateway IP in output
+
+.. option:: --name <name>
+
+    List only subnets of given name in output
+
+.. option:: --subnet-range <subnet-range>
+
+    List only subnets of given subnet range (in CIDR notation) in output
+    e.g.: ``--subnet-range 10.10.0.0/16``
+
 subnet set
 ----------
 
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index f1c7d15d55..8a3f229a2d 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -381,9 +381,41 @@ class ListSubnet(command.Lister):
                    "Must be a valid device owner value for a network port "
                    "(repeat option to list multiple service types)")
         )
+        parser.add_argument(
+            '--project',
+            metavar='<project>',
+            help=_("List only subnets which belong to a given project "
+                   "(name or ID) in output")
+        )
+        identity_common.add_project_domain_option_to_parser(parser)
+        parser.add_argument(
+            '--network',
+            metavar='<network>',
+            help=_("List only subnets which belong to a given network "
+                   "(name or ID) in output")
+        )
+        parser.add_argument(
+            '--gateway',
+            metavar='<gateway>',
+            help=_("List only subnets of given gateway IP in output")
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<name>',
+            help=_("List only subnets of given name in output")
+        )
+        parser.add_argument(
+            '--subnet-range',
+            metavar='<subnet-range>',
+            help=_("List only subnets of given subnet range "
+                   "(in CIDR notation) in output "
+                   "e.g.: --subnet-range 10.10.0.0/16")
+        )
         return parser
 
     def take_action(self, parsed_args):
+        identity_client = self.app.client_manager.identity
+        network_client = self.app.client_manager.network
         filters = {}
         if parsed_args.ip_version:
             filters['ip_version'] = parsed_args.ip_version
@@ -393,7 +425,24 @@ class ListSubnet(command.Lister):
             filters['enable_dhcp'] = False
         if parsed_args.service_types:
             filters['service_types'] = parsed_args.service_types
-        data = self.app.client_manager.network.subnets(**filters)
+        if parsed_args.project:
+            project_id = identity_common.find_project(
+                identity_client,
+                parsed_args.project,
+                parsed_args.project_domain,
+            ).id
+            filters['tenant_id'] = project_id
+        if parsed_args.network:
+            network_id = network_client.find_network(parsed_args.network,
+                                                     ignore_missing=False).id
+            filters['network_id'] = network_id
+        if parsed_args.gateway:
+            filters['gateway_ip'] = parsed_args.gateway
+        if parsed_args.name:
+            filters['name'] = parsed_args.name
+        if parsed_args.subnet_range:
+            filters['cidr'] = parsed_args.subnet_range
+        data = network_client.subnets(**filters)
 
         headers = ('ID', 'Name', 'Network', 'Subnet')
         columns = ('id', 'name', 'network_id', 'cidr')
diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py
index 6d7623443d..f09fe4faa0 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet.py
@@ -646,7 +646,6 @@ class TestListSubnet(TestSubnet):
             ('service_types', ['network:router_gateway']),
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
         columns, data = self.cmd.take_action(parsed_args)
         filters = {'service_types': ['network:router_gateway']}
 
@@ -654,6 +653,24 @@ class TestListSubnet(TestSubnet):
         self.assertEqual(self.columns, columns)
         self.assertEqual(self.data, list(data))
 
+    def test_subnet_list_project(self):
+        project = identity_fakes_v3.FakeProject.create_one_project()
+        self.projects_mock.get.return_value = project
+        arglist = [
+            '--project', project.id,
+        ]
+        verifylist = [
+            ('project', project.id),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        filters = {'tenant_id': project.id}
+
+        self.network.subnets.assert_called_once_with(**filters)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.data, list(data))
+
     def test_subnet_list_service_type_multiple(self):
         arglist = [
             '--service-type', 'network:router_gateway',
@@ -668,6 +685,97 @@ class TestListSubnet(TestSubnet):
         columns, data = self.cmd.take_action(parsed_args)
         filters = {'service_types': ['network:router_gateway',
                                      'network:floatingip_agent_gateway']}
+        self.network.subnets.assert_called_once_with(**filters)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.data, list(data))
+
+    def test_subnet_list_project_domain(self):
+        project = identity_fakes_v3.FakeProject.create_one_project()
+        self.projects_mock.get.return_value = project
+        arglist = [
+            '--project', project.id,
+            '--project-domain', project.domain_id,
+        ]
+        verifylist = [
+            ('project', project.id),
+            ('project_domain', project.domain_id),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        filters = {'tenant_id': project.id}
+
+        self.network.subnets.assert_called_once_with(**filters)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.data, list(data))
+
+    def test_subnet_list_network(self):
+        network = network_fakes.FakeNetwork.create_one_network()
+        self.network.find_network = mock.Mock(return_value=network)
+        arglist = [
+            '--network', network.id,
+        ]
+        verifylist = [
+            ('network', network.id),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        filters = {'network_id': network.id}
+
+        self.network.subnets.assert_called_once_with(**filters)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.data, list(data))
+
+    def test_subnet_list_gateway(self):
+        subnet = network_fakes.FakeSubnet.create_one_subnet()
+        self.network.find_network = mock.Mock(return_value=subnet)
+        arglist = [
+            '--gateway', subnet.gateway_ip,
+        ]
+        verifylist = [
+            ('gateway', subnet.gateway_ip),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        filters = {'gateway_ip': subnet.gateway_ip}
+
+        self.network.subnets.assert_called_once_with(**filters)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.data, list(data))
+
+    def test_subnet_list_name(self):
+        subnet = network_fakes.FakeSubnet.create_one_subnet()
+        self.network.find_network = mock.Mock(return_value=subnet)
+        arglist = [
+            '--name', subnet.name,
+        ]
+        verifylist = [
+            ('name', subnet.name),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        filters = {'name': subnet.name}
+
+        self.network.subnets.assert_called_once_with(**filters)
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.data, list(data))
+
+    def test_subnet_list_subnet_range(self):
+        subnet = network_fakes.FakeSubnet.create_one_subnet()
+        self.network.find_network = mock.Mock(return_value=subnet)
+        arglist = [
+            '--subnet-range', subnet.cidr,
+        ]
+        verifylist = [
+            ('subnet_range', subnet.cidr),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        filters = {'cidr': subnet.cidr}
 
         self.network.subnets.assert_called_once_with(**filters)
         self.assertEqual(self.columns, columns)
diff --git a/releasenotes/notes/bug-1610883-38929f6fc2eefc9a.yaml b/releasenotes/notes/bug-1610883-38929f6fc2eefc9a.yaml
new file mode 100644
index 0000000000..215c24f438
--- /dev/null
+++ b/releasenotes/notes/bug-1610883-38929f6fc2eefc9a.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Make ``subnet list`` command supports listing up subnets with
+    some filtering options by adding ``--project`` and ``--project-domain``,
+    ``--network``, ``--gateway``,``--name``, ``--subnet-range``
+    options to the command.
+    [Bug `1610883 <https://bugs.launchpad.net/bugs/1610883>`_]