Add agent modules and states
Add REST API wrapper for agents, l3_agents and dhcp_agents
Change-Id: I63ce0a4224b7cd4d65ce80f1efd0c5cfb6f0617e
Closes-Issue: PROD-22594
diff --git a/_modules/neutronv2/__init__.py b/_modules/neutronv2/__init__.py
index 6e37241..3626669 100644
--- a/_modules/neutronv2/__init__.py
+++ b/_modules/neutronv2/__init__.py
@@ -5,28 +5,32 @@
except ImportError:
REQUIREMENTS_MET = False
+from neutronv2 import lists
from neutronv2 import networks
from neutronv2 import subnetpools
from neutronv2 import auto_alloc
from neutronv2 import subnets
+from neutronv2 import agents
+from neutronv2 import routers
+
network_get_details = networks.network_get_details
network_update = networks.network_update
network_delete = networks.network_delete
-network_list = networks.network_list
+network_list = lists.network_list
network_create = networks.network_create
network_bulk_create = networks.network_bulk_create
subnetpool_get_details = subnetpools.subnetpool_get_details
subnetpool_update = subnetpools.subnetpool_update
subnetpool_delete = subnetpools.subnetpool_delete
-subnetpool_list = subnetpools.subnetpool_list
+subnetpool_list = lists.subnetpool_list
subnetpool_create = subnetpools.subnetpool_create
auto_alloc_get_details = auto_alloc.auto_alloc_get_details
auto_alloc_delete = auto_alloc.auto_alloc_delete
-subnet_list = subnets.subnet_list
+subnet_list = lists.subnet_list
subnet_create = subnets.subnet_create
subnet_bulk_create = subnets.subnet_bulk_create
subnet_get_details = subnets.subnet_get_details
@@ -34,6 +38,29 @@
subnet_delete = subnets.subnet_delete
+agent_list = lists.agent_list
+agent_get_details = agents.agent_get_details
+agent_update = agents.agent_update
+agent_delete = agents.agent_delete
+l3_agent_router_list = agents.l3_agent_router_list
+l3_agent_router_schedule = agents.l3_agent_router_schedule
+l3_agent_router_remove = agents.l3_agent_router_remove
+l3_agent_by_router_list = agents.l3_agent_by_router_list
+dhcp_agent_list_networks = agents.dhcp_agent_list_networks
+dhcp_agent_network_schedule = agents.dhcp_agent_network_schedule
+dhcp_agent_network_remove = agents.dhcp_agent_network_remove
+dhcp_agent_by_network_list = agents.dhcp_agent_by_network_list
+
+
+router_list = lists.router_list
+router_create = routers.router_create
+router_get_details = routers.router_get_details
+router_update = routers.router_update
+router_delete = routers.router_delete
+router_interface_add = routers.router_interface_add
+router_interface_remove = routers.router_interface_remove
+
+
__all__ = (
'network_get_details', 'network_update', 'network_delete', 'network_list',
'network_create', 'network_bulk_create', 'subnetpool_get_details',
@@ -41,6 +68,13 @@
'subnetpool_create', 'auto_alloc_get_details', 'auto_alloc_delete',
'subnet_list', 'subnet_create', 'subnet_bulk_create', 'subnet_get_details',
'subnet_update', 'subnet_delete',
+ 'agent_list', 'agent_delete', 'agent_get_details', 'agent_update',
+ 'l3_agent_by_router_list', 'l3_agent_router_list',
+ 'l3_agent_router_remove', 'l3_agent_router_schedule',
+ 'dhcp_agent_by_network_list', 'dhcp_agent_list_networks',
+ 'dhcp_agent_network_remove', 'dhcp_agent_network_schedule',
+ 'router_list', 'router_create', 'router_delete', 'router_get_details',
+ 'router_interface_add', 'router_interface_remove', 'router_update',
)
diff --git a/_modules/neutronv2/agents.py b/_modules/neutronv2/agents.py
new file mode 100644
index 0000000..15703d2
--- /dev/null
+++ b/_modules/neutronv2/agents.py
@@ -0,0 +1,89 @@
+from neutronv2.common import send
+from neutronv2.arg_converter import get_by_name_or_uuid_multiple
+
+
+try:
+ from urllib.parse import urlencode
+except ImportError:
+ from urllib import urlencode
+
+
+@send('get')
+def agent_get_details(agent_id, **kwargs):
+ url = '/agents/{}?{}'.format(agent_id, urlencode(kwargs))
+ return url, {}
+
+
+@send('put')
+def agent_update(agent_id, **kwargs):
+ url = '/agents/{}'.format(agent_id)
+ json = {
+ 'agent': kwargs,
+ }
+ return url, {'json': json}
+
+
+@send('delete')
+def agent_delete(agent_id, **kwargs):
+ url = '/agents/{}'.format(agent_id)
+ return url, {}
+
+
+@send('get')
+def l3_agent_router_list(agent_id, **kwargs):
+ url = '/agents/{}/l3-routers'.format(agent_id)
+ return url, {}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('post')
+def l3_agent_router_schedule(router_id, agent_id, **kwargs):
+ url = '/agents/{}/l3-routers'.format(agent_id)
+ json = {
+ 'router_id': router_id,
+ }
+ return url, {'json': json}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('delete')
+def l3_agent_router_remove(router_id, agent_id, **kwargs):
+ url = '/agents/{}/l3-routers/{}'.format(agent_id, router_id)
+ return url, {}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('get')
+def l3_agent_by_router_list(router_id, **kwargs):
+ url = '/routers/{}/l3-agents'.format(router_id)
+ return url, {}
+
+
+@send('get')
+def dhcp_agent_list_networks(agent_id, **kwargs):
+ url = '/agents/{}/dhcp-networks'.format(agent_id)
+ return url, {}
+
+
+@get_by_name_or_uuid_multiple([('network', 'network_id')])
+@send('post')
+def dhcp_agent_network_schedule(network_id, agent_id, **kwargs):
+ url = '/agents/{}/dhcp-networks'.format(agent_id)
+ json = {
+ 'network_id': network_id,
+ }
+ return url, {'json': json}
+
+
+@get_by_name_or_uuid_multiple([('network', 'network_id')])
+@send('delete')
+def dhcp_agent_network_remove(network_id, agent_id, **kwargs):
+ url = '/agents/{}/dhcp-networks/{}'.format(agent_id, network_id)
+ return url, {}
+
+
+@get_by_name_or_uuid_multiple([('network', 'network_id')])
+@send('get')
+def dhcp_agent_by_network_list(network_id, **kwargs):
+ url = '/networks/{}/dhcp-agents'.format(network_id)
+ return url, {}
diff --git a/_modules/neutronv2/arg_converter.py b/_modules/neutronv2/arg_converter.py
new file mode 100644
index 0000000..f2bc76d
--- /dev/null
+++ b/_modules/neutronv2/arg_converter.py
@@ -0,0 +1,61 @@
+from neutronv2 import lists
+from neutronv2 import common
+import functools
+import inspect
+from uuid import UUID
+
+
+def _check_uuid(val):
+ try:
+ return str(UUID(val)) == val
+ except (TypeError, ValueError, AttributeError):
+ return False
+
+
+resource_lists = {
+ 'network': lists.network_list,
+ 'subnet': lists.subnet_list,
+ 'subnetpool': lists.subnetpool_list,
+ 'agent': lists.agent_list,
+ 'router': lists.router_list,
+}
+
+
+response_keys = {
+ 'network': 'networks',
+ 'subnet': 'subnets',
+ 'subnetpool': 'subnetpools',
+ 'agent': 'agents',
+ 'router': 'routers',
+}
+
+
+def get_by_name_or_uuid_multiple(resource_arg_name_pairs):
+ def wrap(func):
+ @functools.wraps(func)
+ def wrapped_f(*args, **kwargs):
+ largs = list(args)
+ inspect_args = inspect.getargspec(
+ func.func_closure[0].cell_contents)
+ for (resource, arg_name) in resource_arg_name_pairs:
+ arg_index = inspect_args.args.index(arg_name)
+ if arg_name in kwargs:
+ ref = kwargs.pop(arg_name, None)
+ else:
+ ref = largs.pop(arg_index)
+ cloud_name = kwargs['cloud_name']
+ if _check_uuid(ref):
+ kwargs[arg_name] = ref
+ else:
+ # Then we have name not uuid
+ resp_key = response_keys[resource]
+ resp = resource_lists[resource](
+ name=ref, cloud_name=cloud_name)[resp_key]
+ if len(resp) == 0:
+ raise common.ResourceNotFound(resp_key, ref)
+ elif len(resp) > 1:
+ raise common.MultipleResourcesFound(resp_key, ref)
+ kwargs[arg_name] = resp[0]['id']
+ return func(*largs, **kwargs)
+ return wrapped_f
+ return wrap
diff --git a/_modules/neutronv2/common.py b/_modules/neutronv2/common.py
index adc3ff5..0dd4b8e 100644
--- a/_modules/neutronv2/common.py
+++ b/_modules/neutronv2/common.py
@@ -1,6 +1,6 @@
+import functools
import logging
import os_client_config
-from uuid import UUID
log = logging.getLogger(__name__)
@@ -63,6 +63,7 @@
def send(method):
def wrap(func):
+ @functools.wraps(func)
def wrapped_f(*args, **kwargs):
cloud_name = kwargs.pop('cloud_name')
if not cloud_name:
@@ -89,37 +90,3 @@
return resp
return wrapped_f
return wrap
-
-
-def _check_uuid(val):
- try:
- return str(UUID(val)) == val
- except (TypeError, ValueError, AttributeError):
- return False
-
-
-def get_by_name_or_uuid(resource_list, resp_key,
- res_id_key='name'):
- def wrap(func):
- def wrapped_f(*args, **kwargs):
- if res_id_key in kwargs:
- ref = kwargs.pop(res_id_key)
- start_arg = 0
- else:
- start_arg = 1
- ref = args[0]
- cloud_name = kwargs['cloud_name']
- if _check_uuid(ref):
- uuid = ref
- else:
- # Then we have name not uuid
- resp = resource_list(
- name=ref, cloud_name=cloud_name)[resp_key]
- if len(resp) == 0:
- raise ResourceNotFound(resp_key, ref)
- elif len(resp) > 1:
- raise MultipleResourcesFound(resp_key, ref)
- uuid = resp[0]['id']
- return func(uuid, *args[start_arg:], **kwargs)
- return wrapped_f
- return wrap
diff --git a/_modules/neutronv2/lists.py b/_modules/neutronv2/lists.py
new file mode 100644
index 0000000..1b56392
--- /dev/null
+++ b/_modules/neutronv2/lists.py
@@ -0,0 +1,36 @@
+from neutronv2.common import send
+
+try:
+ from urllib.parse import urlencode
+except ImportError:
+ from urllib import urlencode
+
+
+@send('get')
+def subnet_list(**kwargs):
+ url = '/subnets?{}'.format(urlencode(kwargs))
+ return url, {}
+
+
+@send('get')
+def subnetpool_list(**kwargs):
+ url = '/subnetpools?{}'.format(urlencode(kwargs))
+ return url, {}
+
+
+@send('get')
+def agent_list(**kwargs):
+ url = '/agents?{}'.format(urlencode(kwargs))
+ return url, {}
+
+
+@send('get')
+def network_list(**kwargs):
+ url = '/networks?{}'.format(urlencode(kwargs))
+ return url, {}
+
+
+@send('get')
+def router_list(**kwargs):
+ url = '/routers?{}'.format(urlencode(kwargs))
+ return url, {}
diff --git a/_modules/neutronv2/networks.py b/_modules/neutronv2/networks.py
index d0e85f8..bf0bf6c 100644
--- a/_modules/neutronv2/networks.py
+++ b/_modules/neutronv2/networks.py
@@ -1,26 +1,20 @@
-from neutronv2.common import send, get_by_name_or_uuid
+from neutronv2.common import send
+from neutronv2.arg_converter import get_by_name_or_uuid_multiple
+
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
-RESOURCE_LIST_KEY = 'networks'
-
-@send('get')
-def network_list(**kwargs):
- url = '/networks?{}'.format(urlencode(kwargs))
- return url, {}
-
-
-@get_by_name_or_uuid(network_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('network', 'network_id')])
@send('get')
def network_get_details(network_id, **kwargs):
url = '/networks/{}?{}'.format(network_id, urlencode(kwargs))
return url, {}
-@get_by_name_or_uuid(network_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('network', 'network_id')])
@send('put')
def network_update(network_id, **kwargs):
url = '/networks/{}'.format(network_id)
@@ -30,7 +24,7 @@
return url, {'json': json}
-@get_by_name_or_uuid(network_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('network', 'network_id')])
@send('delete')
def network_delete(network_id, **kwargs):
url = '/networks/{}'.format(network_id)
diff --git a/_modules/neutronv2/routers.py b/_modules/neutronv2/routers.py
new file mode 100644
index 0000000..5c240aa
--- /dev/null
+++ b/_modules/neutronv2/routers.py
@@ -0,0 +1,56 @@
+from neutronv2.common import send
+from neutronv2.arg_converter import get_by_name_or_uuid_multiple
+
+try:
+ from urllib.parse import urlencode
+except ImportError:
+ from urllib import urlencode
+
+
+@send('post')
+def router_create(name, **kwargs):
+ url = '/routers'
+ json = {
+ 'router': {
+ 'name': name,
+ }
+ }
+ json['router'].update(kwargs)
+ return url, {'json': json}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('get')
+def router_get_details(router_id, **kwargs):
+ url = '/routers/{}?{}'.format(router_id, urlencode(kwargs))
+ return url, {}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('put')
+def router_update(router_id, **kwargs):
+ url = '/routers/{}'.format(router_id)
+ return url, {'json': {'router': kwargs}}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('delete')
+def router_delete(router_id, **kwargs):
+ url = '/routers/{}'.format(router_id)
+ return url, {}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('put')
+def router_interface_add(router_id, **kwargs):
+ url = '/routers/{}/add_role_interface'.format(router_id)
+ json = kwargs
+ return url, {'json': json}
+
+
+@get_by_name_or_uuid_multiple([('router', 'router_id')])
+@send('put')
+def router_interface_remove(router_id, **kwargs):
+ url = '/routers/{}/remove_role_interface'.format(router_id)
+ json = kwargs
+ return url, {'json': json}
diff --git a/_modules/neutronv2/subnetpools.py b/_modules/neutronv2/subnetpools.py
index fb1912b..5e55393 100644
--- a/_modules/neutronv2/subnetpools.py
+++ b/_modules/neutronv2/subnetpools.py
@@ -1,19 +1,13 @@
-from neutronv2.common import send, get_by_name_or_uuid
+from neutronv2.common import send
+from neutronv2.arg_converter import get_by_name_or_uuid_multiple
+
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
-RESOURCE_LIST_KEY = 'subnetpools'
-
-@send('get')
-def subnetpool_list(**kwargs):
- url = '/subnetpools?{}'.format(urlencode(kwargs))
- return url, {}
-
-
-@get_by_name_or_uuid(subnetpool_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('subnetpool', 'subnetpool_id')])
@send('get')
def subnetpool_get_details(subnetpool_id, **kwargs):
url = '/subnetpools/{}?{}'.format(
@@ -22,7 +16,7 @@
return url, {}
-@get_by_name_or_uuid(subnetpool_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('subnetpool', 'subnetpool_id')])
@send('put')
def subnetpool_update(subnetpool_id, **kwargs):
url = '/subnetpools/{}'.format(subnetpool_id)
@@ -32,7 +26,7 @@
return url, {'json': json}
-@get_by_name_or_uuid(subnetpool_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('subnetpool', 'subnetpool_id')])
@send('delete')
def subnetpool_delete(subnetpool_id, **kwargs):
url = '/subnetpools/{}'.format(subnetpool_id)
diff --git a/_modules/neutronv2/subnets.py b/_modules/neutronv2/subnets.py
index 3a29969..7388584 100644
--- a/_modules/neutronv2/subnets.py
+++ b/_modules/neutronv2/subnets.py
@@ -1,21 +1,13 @@
-from neutronv2.common import send, get_by_name_or_uuid
-from neutronv2 import networks
+from neutronv2.common import send
+from neutronv2.arg_converter import get_by_name_or_uuid_multiple
+
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
-RESOURCE_LIST_KEY = 'subnets'
-
-@send('get')
-def subnet_list(**kwargs):
- url = '/subnets?{}'.format(urlencode(kwargs))
- return url, {}
-
-
-@get_by_name_or_uuid(networks.network_list, networks.RESOURCE_LIST_KEY,
- res_id_key='network_id')
+@get_by_name_or_uuid_multiple([('network', 'network_id')])
@send('post')
def subnet_create(network_id, ip_version, cidr, **kwargs):
url = '/subnets'
@@ -39,14 +31,14 @@
return url, {'json': json}
-@get_by_name_or_uuid(subnet_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('subnet', 'subnet_id')])
@send('get')
def subnet_get_details(subnet_id, **kwargs):
url = '/subnets/{}'.format(subnet_id)
return url, {}
-@get_by_name_or_uuid(subnet_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('subnet', 'subnet_id')])
@send('put')
def subnet_update(subnet_id, **kwargs):
url = '/subnets/{}'.format(subnet_id)
@@ -56,7 +48,7 @@
return url, {'json': json}
-@get_by_name_or_uuid(subnet_list, RESOURCE_LIST_KEY)
+@get_by_name_or_uuid_multiple([('subnet', 'subnet_id')])
@send('delete')
def subnet_delete(subnet_id, **kwargs):
url = '/subnets/{}'.format(subnet_id)
diff --git a/_states/neutronv2.py b/_states/neutronv2.py
index 06a0363..42e202b 100644
--- a/_states/neutronv2.py
+++ b/_states/neutronv2.py
@@ -15,7 +15,7 @@
try:
method_name = '{}_get_details'.format(resource)
exact_resource = _neutronv2_call(
- method_name, name=name, cloud_name=cloud_name
+ method_name, name, cloud_name=cloud_name
)[resource]
except Exception as e:
if 'ResourceNotFound' in repr(e):
@@ -54,7 +54,7 @@
try:
method_name = '{}_get_details'.format(resource)
_neutronv2_call(
- method_name, name=name, cloud_name=cloud_name
+ method_name, name, cloud_name=cloud_name
)[resource]
except Exception as e:
if 'ResourceNotFound' in repr(e):
@@ -64,7 +64,7 @@
try:
method_name = '{}_delete'.format(resource)
_neutronv2_call(
- method_name, name=name, cloud_name=cloud_name
+ method_name, name, cloud_name=cloud_name
)
except Exception as e:
log.error('Neutron delete {0} failed with {1}'.format(resource, e))
@@ -117,6 +117,36 @@
return _resource_absent('subnetpool', name, cloud_name)
+def agent_present(name, agent_type, cloud_name, **kwargs):
+ """
+ :param name: agent host name
+ :param agent_type: type of the agent. i.e. 'L3 agent' or 'DHCP agent'
+ :param kwargs:
+ :param description: agent description
+ :param admin_state_up: administrative state of the agent
+ """
+ agents = _neutronv2_call(
+ 'agent_list', host=name, agent_type=agent_type,
+ cloud_name=cloud_name)['agents']
+ # Make sure we have one and only one such agent
+ if len(agents) == 1:
+ agent = agents[0]
+ to_update = {}
+ for key in kwargs:
+ if kwargs[key] != agent[key]:
+ to_update[key] = kwargs[key]
+ if to_update:
+ try:
+ _neutronv2_call('agent_update', agent_id=agent['id'],
+ cloud_name=cloud_name, **kwargs)
+ except Exception:
+ return _failed('update', name, 'agent')
+ return _succeeded('update', name, 'agent')
+ return _succeeded('no_changes', name, 'agent')
+ else:
+ return _failed('find', name, 'agent')
+
+
def _succeeded(op, name, resource, changes=None):
msg_map = {
'create': '{0} {1} created',