From 96f025afce7561e05eb19928c845c25918e4391b Mon Sep 17 00:00:00 2001 From: Ankur Gupta Date: Wed, 12 Oct 2016 22:38:39 -0500 Subject: [PATCH] Network L3 Router Commands SDK implementation of L3 Agent commands which would allow for list, add, remove of routers to L3 Agents. Partially Implements: blueprint network-l3-commands Change-Id: Iab11cfae17153a64f09590b6577c3c800dec0266 --- openstack/network/v2/_proxy.py | 56 +++++++++++++++++ openstack/network/v2/agent.py | 28 +++++++++ openstack/network/v2/router.py | 17 +++++ .../v2/test_agent_add_remove_router.py | 63 +++++++++++++++++++ openstack/tests/unit/network/v2/test_agent.py | 43 +++++++++++++ openstack/tests/unit/network/v2/test_proxy.py | 19 ++++++ .../tests/unit/network/v2/test_router.py | 16 +++++ 7 files changed, 242 insertions(+) create mode 100644 openstack/tests/functional/network/v2/test_agent_add_remove_router.py diff --git a/openstack/network/v2/_proxy.py b/openstack/network/v2/_proxy.py index 1b32a27b8..8640f8129 100644 --- a/openstack/network/v2/_proxy.py +++ b/openstack/network/v2/_proxy.py @@ -2255,6 +2255,62 @@ class Proxy(proxy2.BaseProxy): """ return router.remove_gateway(self.session, **body) + def routers_hosting_l3_agents(self, router, **query): + """Return a generator of L3 agent hosting a router + + :param router: Either the router id or an instance of + :class:`~openstack.network.v2.router.Router` + :param kwargs \*\*query: Optional query parameters to be sent to limit + the resources returned + + :returns: A generator of Router L3 Agents + :rtype: :class:`~openstack.network.v2.router.RouterL3Agents` + """ + router = self._get_resource(_router.Router, router) + return self._list(_agent.RouterL3Agent, paginated=False, + router_id=router.id, **query) + + def agent_hosted_routers(self, agent, **query): + """Return a generator of routers hosted by a L3 agent + + :param agent: Either the agent id of an instance of + :class:`~openstack.network.v2.network_agent.Agent` + :param kwargs \*\*query: Optional query parameters to be sent to limit + the resources returned + + :returns: A generator of routers + :rtype: :class:`~openstack.network.v2.agent.L3AgentRouters` + """ + agent = self._get_resource(_agent.Agent, agent) + return self._list(_router.L3AgentRouter, paginated=False, + agent_id=agent.id, **query) + + def add_router_to_agent(self, agent, router): + """Add router to L3 agent + + :param agent: Either the id of an agent + :class:`~openstack.network.v2.agent.Agent` instance + :param router: A router instance + :returns: Agent with attached router + :rtype: :class:`~openstack.network.v2.agent.Agent` + """ + agent = self._get_resource(_agent.Agent, agent) + router = self._get_resource(_router.Router, router) + return agent.add_router_to_agent(self.session, router.id) + + def remove_router_from_agent(self, agent, router): + """Remove router from L3 agent + + :param agent: Either the id of an agent or an + :class:`~openstack.network.v2.agent.Agent` instance + :param router: A router instance + :returns: Agent with removed router + :rtype: :class:`~openstack.network.v2.agent.Agent` + """ + agent = self._get_resource(_agent.Agent, agent) + router = self._get_resource(_router.Router, router) + return agent.remove_router_from_agent(self.session, router.id) + def create_security_group(self, **attrs): """Create a new security group from attributes diff --git a/openstack/network/v2/agent.py b/openstack/network/v2/agent.py index 549456afa..e31123a5b 100644 --- a/openstack/network/v2/agent.py +++ b/openstack/network/v2/agent.py @@ -75,6 +75,17 @@ class Agent(resource.Resource): network_id) session.delete(url, endpoint_filter=self.service, json=body) + def add_router_to_agent(self, session, router): + body = {'router_id': router} + url = utils.urljoin(self.base_path, self.id, 'l3-routers') + resp = session.post(url, endpoint_filter=self.service, json=body) + return resp.json() + + def remove_router_from_agent(self, session, router): + body = {'router_id': router} + url = utils.urljoin(self.base_path, self.id, 'l3-routers', router) + session.delete(url, endpoint_filter=self.service, json=body) + class NetworkHostingDHCPAgent(Agent): resource_key = 'agent' @@ -91,3 +102,20 @@ class NetworkHostingDHCPAgent(Agent): allow_list = True # NOTE: Doesn't support query yet. + + +class RouterL3Agent(Agent): + resource_key = 'agent' + resources_key = 'agents' + base_path = '/routers/%(router_id)s/l3-agents' + resource_name = 'l3-agent' + service = network_service.NetworkService() + + # capabilities + allow_create = False + allow_retrieve = True + allow_update = False + allow_delete = False + allow_list = True + + # NOTE: No query parameter is supported diff --git a/openstack/network/v2/router.py b/openstack/network/v2/router.py index 5043ce13a..89539110f 100644 --- a/openstack/network/v2/router.py +++ b/openstack/network/v2/router.py @@ -128,3 +128,20 @@ class Router(resource.Resource): 'remove_gateway_router') resp = session.put(url, endpoint_filter=self.service, json=body) return resp.json() + + +class L3AgentRouter(Router): + resource_key = 'router' + resources_key = 'routers' + base_path = '/agents/%(agent_id)s/l3-routers' + resource_name = 'l3-router' + service = network_service.NetworkService() + + # capabilities + allow_create = False + allow_retrieve = True + allow_update = False + allow_delete = False + allow_list = True + +# NOTE: No query parameter is supported diff --git a/openstack/tests/functional/network/v2/test_agent_add_remove_router.py b/openstack/tests/functional/network/v2/test_agent_add_remove_router.py new file mode 100644 index 000000000..4f4f4c669 --- /dev/null +++ b/openstack/tests/functional/network/v2/test_agent_add_remove_router.py @@ -0,0 +1,63 @@ +# 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. + +import uuid + +from openstack.network.v2 import router +from openstack.tests.functional import base + + +class TestAgentRouters(base.BaseFunctionalTest): + + ROUTER_NAME = 'router-name-' + uuid.uuid4().hex + ROUTER_ID = None + AGENT = None + AGENT_ID = None + + @classmethod + def setUpClass(cls): + super(TestAgentRouters, cls).setUpClass() + + rot = cls.conn.network.create_router(name=cls.ROUTER_NAME) + assert isinstance(rot, router.Router) + cls.ROUTER_ID = rot.id + agent_list = list(cls.conn.network.agents()) + agents = [agent for agent in agent_list + if agent.agent_type == 'L3 agent'] + cls.AGENT = agents[0] + cls.AGENT_ID = cls.AGENT.id + + @classmethod + def tearDownClass(cls): + rot = cls.conn.network.delete_router(cls.ROUTER_ID, + ignore_missing=False) + cls.assertIs(None, rot) + + def test_add_router_to_agent(self): + sot = self.AGENT.add_router_to_agent(self.conn.session, + router_id=self.ROUTER_ID) + self._verify_add(sot) + + def test_remove_router_from_agent(self): + sot = self.AGENT.remove_router_from_agent(self.conn.session, + router_id=self.ROUTER_ID) + self._verify_remove(sot) + + def _verify_add(self, sot): + rots = self.conn.network.agent_hosted_routers(self.AGENT_ID) + routers = [router.id for router in rots] + self.assertIn(self.ROUTER_ID, routers) + + def _verify_remove(self, sot): + rots = self.conn.network.agent_hosted_routers(self.AGENT_ID) + routers = [router.id for router in rots] + self.assertNotIn(self.ROUTER_ID, routers) diff --git a/openstack/tests/unit/network/v2/test_agent.py b/openstack/tests/unit/network/v2/test_agent.py index 003520b7a..0ab82ebae 100644 --- a/openstack/tests/unit/network/v2/test_agent.py +++ b/openstack/tests/unit/network/v2/test_agent.py @@ -89,6 +89,33 @@ class TestAgent(testtools.TestCase): sess.delete.assert_called_with('agents/IDENTIFIER/dhcp-networks/', endpoint_filter=net.service, json=body) + def test_add_router_to_agent(self): + # Add router to agent + sot = agent.Agent(**EXAMPLE) + response = mock.Mock() + response.body = {'router_id': '1'} + response.json = mock.Mock(return_value=response.body) + sess = mock.Mock() + sess.post = mock.Mock(return_value=response) + router_id = '1' + self.assertEqual(response.body, + sot.add_router_to_agent(sess, router_id)) + body = {'router_id': router_id} + url = 'agents/IDENTIFIER/l3-routers' + sess.post.assert_called_with(url, endpoint_filter=sot.service, + json=body) + + def test_remove_router_from_agent(self): + # Remove router from agent + sot = agent.Agent(**EXAMPLE) + sess = mock.Mock() + router_id = {} + self.assertIsNone(sot.remove_router_from_agent(sess, router_id)) + body = {'router_id': {}} + + sess.delete.assert_called_with('agents/IDENTIFIER/l3-routers/', + endpoint_filter=sot.service, json=body) + class TestNetworkHostingDHCPAgent(testtools.TestCase): @@ -104,3 +131,19 @@ class TestNetworkHostingDHCPAgent(testtools.TestCase): self.assertFalse(net.allow_update) self.assertFalse(net.allow_delete) self.assertTrue(net.allow_list) + + +class TestRouterL3Agent(testtools.TestCase): + + def test_basic(self): + sot = agent.RouterL3Agent() + self.assertEqual('agent', sot.resource_key) + self.assertEqual('agents', sot.resources_key) + self.assertEqual('/routers/%(router_id)s/l3-agents', sot.base_path) + self.assertEqual('l3-agent', sot.resource_name) + self.assertEqual('network', sot.service.service_type) + self.assertFalse(sot.allow_create) + self.assertTrue(sot.allow_retrieve) + self.assertFalse(sot.allow_update) + self.assertFalse(sot.allow_delete) + self.assertTrue(sot.allow_list) diff --git a/openstack/tests/unit/network/v2/test_proxy.py b/openstack/tests/unit/network/v2/test_proxy.py index c01e79be1..7779ea27a 100644 --- a/openstack/tests/unit/network/v2/test_proxy.py +++ b/openstack/tests/unit/network/v2/test_proxy.py @@ -55,6 +55,7 @@ QOS_POLICY_ID = 'qos-policy-id-' + uuid.uuid4().hex QOS_RULE_ID = 'qos-rule-id-' + uuid.uuid4().hex NETWORK_ID = 'network-id-' + uuid.uuid4().hex AGENT_ID = 'agent-id-' + uuid.uuid4().hex +ROUTER_ID = 'router-id-' + uuid.uuid4().hex class TestNetworkProxy(test_proxy_base2.TestProxyBase): @@ -739,6 +740,24 @@ class TestNetworkProxy(test_proxy_base2.TestProxyBase): def test_router_update(self): self.verify_update(self.proxy.update_router, router.Router) + def test_router_hosting_l3_agents_list(self): + self.verify_list( + self.proxy.routers_hosting_l3_agents, + agent.RouterL3Agent, + paginated=False, + method_kwargs={'router': ROUTER_ID}, + expected_kwargs={'router_id': ROUTER_ID}, + ) + + def test_agent_hosted_routers_list(self): + self.verify_list( + self.proxy.agent_hosted_routers, + router.L3AgentRouter, + paginated=False, + method_kwargs={'agent': AGENT_ID}, + expected_kwargs={'agent_id': AGENT_ID}, + ) + def test_security_group_create_attrs(self): self.verify_create(self.proxy.create_security_group, security_group.SecurityGroup) diff --git a/openstack/tests/unit/network/v2/test_router.py b/openstack/tests/unit/network/v2/test_router.py index 900f144ef..5fb87f16c 100644 --- a/openstack/tests/unit/network/v2/test_router.py +++ b/openstack/tests/unit/network/v2/test_router.py @@ -203,3 +203,19 @@ class TestRouter(testtools.TestCase): url = 'routers/IDENTIFIER/remove_gateway_router' sess.put.assert_called_with(url, endpoint_filter=sot.service, json=body) + + +class TestL3AgentRouters(testtools.TestCase): + + def test_basic(self): + sot = router.L3AgentRouter() + self.assertEqual('router', sot.resource_key) + self.assertEqual('routers', sot.resources_key) + self.assertEqual('/agents/%(agent_id)s/l3-routers', sot.base_path) + self.assertEqual('l3-router', sot.resource_name) + self.assertEqual('network', sot.service.service_type) + self.assertFalse(sot.allow_create) + self.assertTrue(sot.allow_retrieve) + self.assertFalse(sot.allow_update) + self.assertFalse(sot.allow_delete) + self.assertTrue(sot.allow_list)