Implement network agents
This commit adds api support for network agents. Change-Id: I9755637f76787d5fac8ff295ae273b308fcb98d0 Implements: blueprint implement-network-agents
This commit is contained in:
@@ -76,6 +76,19 @@ provide external network access for servers on project networks.
|
|||||||
|
|
||||||
Full example: `network resource list`_
|
Full example: `network resource list`_
|
||||||
|
|
||||||
|
List Network Agents
|
||||||
|
------------
|
||||||
|
|
||||||
|
A **network agent** is a plugin that handles various tasks used to
|
||||||
|
implement virtual networks. These agents include neutron-dhcp-agent,
|
||||||
|
neutron-l3-agent, neutron-metering-agent, and neutron-lbaas-agent,
|
||||||
|
among others.
|
||||||
|
|
||||||
|
.. literalinclude:: ../examples/network/list.py
|
||||||
|
:pyobject: list_network_agents
|
||||||
|
|
||||||
|
Full example: `network resource list`_
|
||||||
|
|
||||||
Create Network
|
Create Network
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@ Network Resources
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
v2/address_scope
|
v2/address_scope
|
||||||
|
v2/agent
|
||||||
v2/availability_zone
|
v2/availability_zone
|
||||||
v2/extension
|
v2/extension
|
||||||
v2/floating_ip
|
v2/floating_ip
|
||||||
|
12
doc/source/users/resources/network/v2/agent.rst
Normal file
12
doc/source/users/resources/network/v2/agent.rst
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
openstack.network.v2.network
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. automodule:: openstack.network.v2.agent
|
||||||
|
|
||||||
|
The Agent Class
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The ``Agent`` class inherits from :class:`~openstack.resource.Resource`.
|
||||||
|
|
||||||
|
.. autoclass:: openstack.network.v2.agent.Agent
|
||||||
|
:members:
|
@@ -50,3 +50,10 @@ def list_routers(conn):
|
|||||||
|
|
||||||
for router in conn.network.routers():
|
for router in conn.network.routers():
|
||||||
print(router)
|
print(router)
|
||||||
|
|
||||||
|
|
||||||
|
def list_network_agents(conn):
|
||||||
|
print("List Network Agents:")
|
||||||
|
|
||||||
|
for agent in conn.network.agents():
|
||||||
|
print(agent)
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from openstack.network.v2 import address_scope as _address_scope
|
from openstack.network.v2 import address_scope as _address_scope
|
||||||
|
from openstack.network.v2 import agent as _agent
|
||||||
from openstack.network.v2 import availability_zone
|
from openstack.network.v2 import availability_zone
|
||||||
from openstack.network.v2 import extension
|
from openstack.network.v2 import extension
|
||||||
from openstack.network.v2 import floating_ip as _floating_ip
|
from openstack.network.v2 import floating_ip as _floating_ip
|
||||||
@@ -122,6 +123,59 @@ class Proxy(proxy.BaseProxy):
|
|||||||
address_scope,
|
address_scope,
|
||||||
**attrs)
|
**attrs)
|
||||||
|
|
||||||
|
def agents(self, **query):
|
||||||
|
"""Return a generator of network agents
|
||||||
|
|
||||||
|
:param kwargs \*\*query: Optional query parameters to be sent to limit
|
||||||
|
the resources being returned.
|
||||||
|
|
||||||
|
:returns: A generator of agents
|
||||||
|
:rtype: :class:`~openstack.network.v2.agent.Agent`
|
||||||
|
"""
|
||||||
|
return self._list(_agent.Agent, paginated=False, **query)
|
||||||
|
|
||||||
|
def delete_agent(self, agent, ignore_missing=True):
|
||||||
|
"""Delete a network agent
|
||||||
|
|
||||||
|
:param agent: The value can be the ID of a agent or a
|
||||||
|
:class:`~openstack.network.v2.agent.Agent` instance.
|
||||||
|
:param bool ignore_missing: When set to ``False``
|
||||||
|
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||||
|
raised when the agent does not exist.
|
||||||
|
When set to ``True``, no exception will be set when
|
||||||
|
attempting to delete a nonexistent agent.
|
||||||
|
|
||||||
|
:returns: ``None``
|
||||||
|
"""
|
||||||
|
self._delete(_agent.Agent, agent,
|
||||||
|
ignore_missing=ignore_missing)
|
||||||
|
|
||||||
|
def get_agent(self, agent, ignore_missing=True):
|
||||||
|
"""Get a single network agent
|
||||||
|
|
||||||
|
:param agent: The value can be the ID of a agent or a
|
||||||
|
:class:`~openstack.network.v2.agent.Agent` instance.
|
||||||
|
|
||||||
|
:returns: One :class:`~openstack.network.v2.agent.Agent`
|
||||||
|
:rtype: :class:`~openstack.network.v2.agent.Agent`
|
||||||
|
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
||||||
|
when no resource can be found.
|
||||||
|
"""
|
||||||
|
return self._get(_agent.Agent, agent)
|
||||||
|
|
||||||
|
def update_agent(self, agent, **attrs):
|
||||||
|
"""Update a network agent
|
||||||
|
|
||||||
|
:param agent: The value can be the ID of a agent or a
|
||||||
|
:class:`~openstack.network.v2.agent.Agent` instance.
|
||||||
|
:attrs kwargs: The attributes to update on the agent represented
|
||||||
|
by ``value``.
|
||||||
|
|
||||||
|
:returns: One :class:`~openstack.network.v2.agent.Agent`
|
||||||
|
:rtype: :class:`~openstack.network.v2.agent.Agent`
|
||||||
|
"""
|
||||||
|
return self._update(_agent.Agent, agent, **attrs)
|
||||||
|
|
||||||
def availability_zones(self):
|
def availability_zones(self):
|
||||||
"""Return a generator of availability zones
|
"""Return a generator of availability zones
|
||||||
|
|
||||||
|
61
openstack/network/v2/agent.py
Normal file
61
openstack/network/v2/agent.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from openstack import format
|
||||||
|
from openstack.network import network_service
|
||||||
|
from openstack import resource
|
||||||
|
|
||||||
|
|
||||||
|
class Agent(resource.Resource):
|
||||||
|
resource_key = 'agent'
|
||||||
|
resources_key = 'agents'
|
||||||
|
base_path = '/agents'
|
||||||
|
service = network_service.NetworkService()
|
||||||
|
|
||||||
|
# capabilities
|
||||||
|
allow_create = False
|
||||||
|
allow_retrieve = True
|
||||||
|
allow_update = True
|
||||||
|
allow_delete = True
|
||||||
|
allow_list = True
|
||||||
|
|
||||||
|
# Properties
|
||||||
|
#: The type of network agent.
|
||||||
|
agent_type = resource.prop('agent_type')
|
||||||
|
#: Availability zone for the network agent.
|
||||||
|
availability_zone = resource.prop('availability_zone')
|
||||||
|
#: The name of the network agent's application binary.
|
||||||
|
binary = resource.prop('binary')
|
||||||
|
#: Network agent configuration data specific to the agent_type.
|
||||||
|
configuration = resource.prop('configurations')
|
||||||
|
#: Timestamp when the network agent was created.
|
||||||
|
#: *Type: datetime object parsed from ISO 8601 formatted string*
|
||||||
|
created_at = resource.prop('created_at', type=format.ISO8601)
|
||||||
|
#: The network agent description.
|
||||||
|
description = resource.prop('description')
|
||||||
|
#: Timestamp when the network agent's heartbeat was last seen.
|
||||||
|
#: *Type: datetime object parsed from ISO 8601 formatted string*
|
||||||
|
last_heartbeat_at = resource.prop('heartbeat_timestamp',
|
||||||
|
type=format.ISO8601)
|
||||||
|
#: The host the agent is running on.
|
||||||
|
host = resource.prop('host')
|
||||||
|
#: The administrative state of the network agent, which is up
|
||||||
|
#: ``True`` or down ``False``. *Type: bool*
|
||||||
|
is_admin_state_up = resource.prop('admin_state_up', type=bool)
|
||||||
|
#: Whether or not the network agent is alive.
|
||||||
|
#: *Type: bool*
|
||||||
|
is_alive = resource.prop('alive', type=bool)
|
||||||
|
#: Timestamp when the network agent was last started.
|
||||||
|
#: *Type: datetime object parsed from ISO 8601 formatted string*
|
||||||
|
started_at = resource.prop('started_at', type=format.ISO8601)
|
||||||
|
#: The messaging queue topic the network agent subscribes to.
|
||||||
|
topic = resource.prop('topic')
|
51
openstack/tests/functional/network/v2/test_agent.py
Normal file
51
openstack/tests/functional/network/v2/test_agent.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# 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 agent
|
||||||
|
from openstack.tests.functional import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestAgent(base.BaseFunctionalTest):
|
||||||
|
|
||||||
|
AGENT = None
|
||||||
|
DESC = 'test descrition'
|
||||||
|
|
||||||
|
def validate_uuid(self, s):
|
||||||
|
try:
|
||||||
|
uuid.UUID(s)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestAgent, cls).setUpClass()
|
||||||
|
agent_list = list(cls.conn.network.agents())
|
||||||
|
cls.AGENT = agent_list[0]
|
||||||
|
assert isinstance(cls.AGENT, agent.Agent)
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
agent_list = list(self.conn.network.agents())
|
||||||
|
self.AGENT = agent_list[0]
|
||||||
|
assert isinstance(self.AGENT, agent.Agent)
|
||||||
|
self.assertTrue(self.validate_uuid(self.AGENT.id))
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
sot = self.conn.network.get_agent(self.AGENT.id)
|
||||||
|
self.assertEqual(self.AGENT.id, sot.id)
|
||||||
|
|
||||||
|
def test_update(self):
|
||||||
|
sot = self.conn.network.update_agent(self.AGENT.id,
|
||||||
|
description=self.DESC)
|
||||||
|
self.assertEqual(self.DESC, sot.description)
|
73
openstack/tests/unit/network/v2/test_agent.py
Normal file
73
openstack/tests/unit/network/v2/test_agent.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# 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 datetime
|
||||||
|
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
from openstack.network.v2 import agent
|
||||||
|
|
||||||
|
IDENTIFIER = 'IDENTIFIER'
|
||||||
|
EXAMPLE = {
|
||||||
|
'admin_state_up': True,
|
||||||
|
'agent_type': 'Test Agent',
|
||||||
|
'alive': True,
|
||||||
|
'availability_zone': 'az1',
|
||||||
|
'binary': 'test-binary',
|
||||||
|
'configurations': {'attr1': 'value1', 'attr2': 'value2'},
|
||||||
|
'created_at': '2016-03-09T12:14:57.233772',
|
||||||
|
'description': 'test description',
|
||||||
|
'heartbeat_timestamp': '2016-08-09T12:14:57.233772',
|
||||||
|
'host': 'test-host',
|
||||||
|
'id': IDENTIFIER,
|
||||||
|
'started_at': '2016-07-09T12:14:57.233772',
|
||||||
|
'topic': 'test-topic'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestAgent(testtools.TestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
sot = agent.Agent()
|
||||||
|
self.assertEqual('agent', sot.resource_key)
|
||||||
|
self.assertEqual('agents', sot.resources_key)
|
||||||
|
self.assertEqual('/agents', sot.base_path)
|
||||||
|
self.assertEqual('network', sot.service.service_type)
|
||||||
|
self.assertFalse(sot.allow_create)
|
||||||
|
self.assertTrue(sot.allow_retrieve)
|
||||||
|
self.assertTrue(sot.allow_update)
|
||||||
|
self.assertTrue(sot.allow_delete)
|
||||||
|
self.assertTrue(sot.allow_list)
|
||||||
|
|
||||||
|
def test_make_it(self):
|
||||||
|
sot = agent.Agent(EXAMPLE)
|
||||||
|
self.assertTrue(sot.is_admin_state_up)
|
||||||
|
self.assertEqual(EXAMPLE['agent_type'], sot.agent_type)
|
||||||
|
self.assertTrue(sot.is_alive)
|
||||||
|
self.assertEqual(EXAMPLE['availability_zone'],
|
||||||
|
sot.availability_zone)
|
||||||
|
self.assertEqual(EXAMPLE['binary'], sot.binary)
|
||||||
|
self.assertEqual(EXAMPLE['configurations'], sot.configuration)
|
||||||
|
dt = datetime.datetime(2016, 3, 9, 12, 14, 57, 233772).replace(
|
||||||
|
tzinfo=None)
|
||||||
|
self.assertEqual(dt, sot.created_at.replace(tzinfo=None))
|
||||||
|
self.assertEqual(EXAMPLE['description'], sot.description)
|
||||||
|
dt = datetime.datetime(2016, 8, 9, 12, 14, 57, 233772).replace(
|
||||||
|
tzinfo=None)
|
||||||
|
self.assertEqual(dt,
|
||||||
|
sot.last_heartbeat_at.replace(tzinfo=None))
|
||||||
|
self.assertEqual(EXAMPLE['host'], sot.host)
|
||||||
|
self.assertEqual(EXAMPLE['id'], sot.id)
|
||||||
|
dt = datetime.datetime(2016, 7, 9, 12, 14, 57, 233772).replace(
|
||||||
|
tzinfo=None)
|
||||||
|
self.assertEqual(dt, sot.started_at.replace(tzinfo=None))
|
||||||
|
self.assertEqual(EXAMPLE['topic'], sot.topic)
|
@@ -14,6 +14,7 @@ import mock
|
|||||||
|
|
||||||
from openstack.network.v2 import _proxy
|
from openstack.network.v2 import _proxy
|
||||||
from openstack.network.v2 import address_scope
|
from openstack.network.v2 import address_scope
|
||||||
|
from openstack.network.v2 import agent
|
||||||
from openstack.network.v2 import availability_zone
|
from openstack.network.v2 import availability_zone
|
||||||
from openstack.network.v2 import extension
|
from openstack.network.v2 import extension
|
||||||
from openstack.network.v2 import floating_ip
|
from openstack.network.v2 import floating_ip
|
||||||
@@ -73,6 +74,19 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase):
|
|||||||
self.verify_update(self.proxy.update_address_scope,
|
self.verify_update(self.proxy.update_address_scope,
|
||||||
address_scope.AddressScope)
|
address_scope.AddressScope)
|
||||||
|
|
||||||
|
def test_agent_delete(self):
|
||||||
|
self.verify_delete(self.proxy.delete_agent, agent.Agent, True)
|
||||||
|
|
||||||
|
def test_agent_get(self):
|
||||||
|
self.verify_get(self.proxy.get_agent, agent.Agent)
|
||||||
|
|
||||||
|
def test_agents(self):
|
||||||
|
self.verify_list(self.proxy.agents, agent.Agent,
|
||||||
|
paginated=False)
|
||||||
|
|
||||||
|
def test_agent_update(self):
|
||||||
|
self.verify_update(self.proxy.update_agent, agent.Agent)
|
||||||
|
|
||||||
def test_availability_zones(self):
|
def test_availability_zones(self):
|
||||||
self.verify_list_no_kwargs(self.proxy.availability_zones,
|
self.verify_list_no_kwargs(self.proxy.availability_zones,
|
||||||
availability_zone.AvailabilityZone,
|
availability_zone.AvailabilityZone,
|
||||||
|
Reference in New Issue
Block a user