From 1b07201fcf508b79dcf40258bb466ded3e4722f9 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 7 Mar 2019 15:57:29 -0500 Subject: [PATCH] Neutron Router Rebalancing on L3 Agents When a host goes disabled, move the routers off of the l3 agent of that host, distributing them amongst the other available L3 agents. When a host goes enabled, rebalance the routers on the available l3 agents, moving some to the newly enabled host. Change-Id: I50fb3900ada74dd645ec38e1e681bd25ef951d54 Story: 2003857 Task: 26664 Depends-On: I710a698ebb31d48060903fb3966fa379b56ad190 Signed-off-by: Kevin Smith --- .../nfvi_plugins/nfvi_infrastructure_api.py | 52 + .../nfvi_plugins/nfvi_network_api.py | 447 ++++++++ .../nfvi_plugins/openstack/neutron.py | 125 ++- .../nfvi_plugins/openstack/sysinv.py | 19 + .../tests/test_network_rebalance.py | 522 +++++++++ nfv/nfv-vim/nfv_vim/config.ini | 5 + nfv/nfv-vim/nfv_vim/debug.ini | 1 + .../nfv_vim/host_fsm/_host_task_work.py | 23 + .../nfv_vim/network_rebalance/__init__.py | 8 + .../network_rebalance/_network_rebalance.py | 990 ++++++++++++++++++ nfv/nfv-vim/nfv_vim/nfvi/__init__.py | 7 + .../nfvi/_nfvi_infrastructure_module.py | 10 + .../nfv_vim/nfvi/_nfvi_network_module.py | 54 + .../nfvi/api/v1/_nfvi_infrastructure_api.py | 7 + .../nfv_vim/nfvi/api/v1/_nfvi_network_api.py | 42 + nfv/nfv-vim/nfv_vim/vim.py | 3 + 16 files changed, 2306 insertions(+), 9 deletions(-) mode change 100755 => 100644 nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/neutron.py create mode 100644 nfv/nfv-tests/nfv_unit_tests/tests/test_network_rebalance.py create mode 100644 nfv/nfv-vim/nfv_vim/network_rebalance/__init__.py create mode 100644 nfv/nfv-vim/nfv_vim/network_rebalance/_network_rebalance.py diff --git a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_infrastructure_api.py b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_infrastructure_api.py index 8aad2f08..2875f364 100755 --- a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_infrastructure_api.py +++ b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_infrastructure_api.py @@ -178,6 +178,58 @@ class NFVIInfrastructureAPI(nfvi.api.v1.NFVIInfrastructureAPI): (self._openstack_directory.get_service_info( OPENSTACK_SERVICE.NOVA) is not None)) + def get_datanetworks(self, future, host_uuid, callback): + """ + Get host data networks from the plugin + """ + response = dict() + response['completed'] = False + response['reason'] = '' + + try: + future.set_timeouts(config.CONF.get('nfvi-timeouts', None)) + + if self._platform_token is None or \ + self._platform_token.is_expired(): + future.work(openstack.get_token, self._platform_directory) + future.result = (yield) + + if not future.result.is_complete() or \ + future.result.data is None: + DLOG.error("OpenStack get-token did not complete.") + return + + self._platform_token = future.result.data + + future.work(sysinv.get_datanetworks, self._platform_token, + host_uuid) + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("SysInv get-datanetworks did not complete.") + return + + response['result-data'] = future.result.data + response['completed'] = True + + except exceptions.OpenStackRestAPIException as e: + if httplib.UNAUTHORIZED == e.http_status_code: + response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED + if self._platform_token is not None: + self._platform_token.set_expired() + + else: + DLOG.exception("Caught exception while trying to get host %s data " + "networks, error=%s." % (host_uuid, e)) + + except Exception as e: + DLOG.exception("Caught exception while trying to get host %s data networks, " + "error=%s." % (host_uuid, e)) + + finally: + callback.send(response) + callback.close() + def get_system_info(self, future, callback): """ Get information about the system from the plugin diff --git a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_network_api.py b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_network_api.py index 97a04b3f..36e7bd8c 100755 --- a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_network_api.py +++ b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_network_api.py @@ -1109,6 +1109,452 @@ class NFVINetworkAPI(nfvi.api.v1.NFVINetworkAPI): callback.send(response) callback.close() + def get_network_agents(self, future, callback): + """ + Get Network Agent Information for all agents on all hosts. + """ + response = dict() + response['completed'] = False + response['result-data'] = '' + response['reason'] = '' + + try: + future.set_timeouts(config.CONF.get('nfvi-timeouts', None)) + + if self._token is None or \ + self._token.is_expired(): + future.work(openstack.get_token, self._directory) + future.result = (yield) + + if not future.result.is_complete() or \ + future.result.data is None: + DLOG.error("OpenStack get-token did not complete") + return + + self._token = future.result.data + + if self._neutron_extensions is None: + future.work(neutron.get_extensions, self._token) + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-extensions did not complete.") + return + + self._neutron_extensions = future.result.data + + if neutron.lookup_extension(neutron.EXTENSION_NAMES.AGENT, + self._neutron_extensions): + # Send Query request to Neutron + future.work(neutron.get_network_agents, + self._token) + + future.result = (yield) + if not future.result.is_complete(): + DLOG.error("Neutron get-network-agents failed, " + "operation did not complete") + return + else: + response['result-data'] = future.result.data + else: + DLOG.warn("Neutron Agent Extension not available") + return + + response['completed'] = True + + except exceptions.OpenStackRestAPIException as e: + if httplib.UNAUTHORIZED == e.http_status_code: + response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED + if self._token is not None: + self._token.set_expired() + + else: + DLOG.exception("Caught exception while trying to get " + "neutron network agents, error=%s." % e) + + except Exception as e: + DLOG.exception("Caught exception while trying to get " + "neutron network agents, error=%s." % e) + + finally: + callback.send(response) + callback.close() + + def get_agent_routers(self, future, agent_id, callback): + """ + Get Routers hosted by Network Agent. + """ + response = dict() + response['completed'] = False + response['result-data'] = '' + response['reason'] = '' + + try: + future.set_timeouts(config.CONF.get('nfvi-timeouts', None)) + + if self._token is None or \ + self._token.is_expired(): + future.work(openstack.get_token, self._directory) + future.result = (yield) + + if not future.result.is_complete() or \ + future.result.data is None: + DLOG.error("OpenStack get-token did not complete, " + "agent_id=%s." % agent_id) + return + + self._token = future.result.data + + if self._neutron_extensions is None: + future.work(neutron.get_extensions, self._token) + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-extensions did not complete.") + return + + self._neutron_extensions = future.result.data + + if neutron.lookup_extension(neutron.EXTENSION_NAMES.AGENT, + self._neutron_extensions): + # Send Query request to Neutron + future.work(neutron.get_agent_routers, + self._token, agent_id) + + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-agent-routers failed, " + "operation did not complete, agent_id=%s" + % agent_id) + return + else: + response['result-data'] = future.result.data + else: + DLOG.warn("Neutron Agent Extension not available") + return + + response['completed'] = True + + except exceptions.OpenStackRestAPIException as e: + if httplib.UNAUTHORIZED == e.http_status_code: + response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED + if self._token is not None: + self._token.set_expired() + + else: + DLOG.exception("Caught exception while trying to get " + "agent routers, error=%s." % e) + + except Exception as e: + DLOG.exception("Caught exception while trying to get %s " + "neutron agent routers, error=%s." + % (agent_id, e)) + + finally: + callback.send(response) + callback.close() + + def get_router_ports(self, future, router_id, callback): + """ + Get Ports on a Router + """ + response = dict() + response['completed'] = False + response['result-data'] = '' + response['reason'] = '' + + try: + future.set_timeouts(config.CONF.get('nfvi-timeouts', None)) + + if self._token is None or \ + self._token.is_expired(): + future.work(openstack.get_token, self._directory) + future.result = (yield) + + if not future.result.is_complete() or \ + future.result.data is None: + DLOG.error("OpenStack get-token did not complete, " + "router_id=%s." % router_id) + return + + self._token = future.result.data + + if self._neutron_extensions is None: + future.work(neutron.get_extensions, self._token) + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-extensions did not complete.") + return + + self._neutron_extensions = future.result.data + + if neutron.lookup_extension(neutron.EXTENSION_NAMES.AGENT, + self._neutron_extensions): + # Send Query request to Neutron + future.work(neutron.get_router_ports, + self._token, router_id) + + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-router-ports failed, " + "operation did not complete, router_id=%s" + % router_id) + return + else: + response['result-data'] = future.result.data + else: + DLOG.warn("Neutron Agent Extension not available") + return + + response['completed'] = True + + except exceptions.OpenStackRestAPIException as e: + if httplib.UNAUTHORIZED == e.http_status_code: + response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED + if self._token is not None: + self._token.set_expired() + + else: + DLOG.exception("Caught exception while trying to get " + "router ports, error=%s." % e) + + except Exception as e: + DLOG.exception("Caught exception while trying to get %s " + "router ports, error=%s." + % (router_id, e)) + + finally: + callback.send(response) + callback.close() + + def add_router_to_agent(self, future, agent_id, router_id, callback): + """ + Add a router to an L3 Agent. + """ + response = dict() + response['completed'] = False + response['result-data'] = '' + response['reason'] = '' + + try: + future.set_timeouts(config.CONF.get('nfvi-timeouts', None)) + + if self._token is None or \ + self._token.is_expired(): + future.work(openstack.get_token, self._directory) + future.result = (yield) + + if not future.result.is_complete() or \ + future.result.data is None: + DLOG.error("OpenStack get-token did not complete, " + "router_id=%s." % router_id) + return + + self._token = future.result.data + + if self._neutron_extensions is None: + future.work(neutron.get_extensions, self._token) + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-extensions did not complete.") + return + + self._neutron_extensions = future.result.data + + if neutron.lookup_extension(neutron.EXTENSION_NAMES.AGENT, + self._neutron_extensions): + # Send Query request to Neutron + future.work(neutron.add_router_to_agent, + self._token, agent_id, router_id) + + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron add-router-to-agent failed, " + "operation did not complete, agent_id=%s " + "router_id=%s" % (agent_id, router_id)) + return + else: + response['result-data'] = future.result.data + else: + DLOG.warn("Neutron Agent Extension not available") + return + + response['completed'] = True + + except exceptions.OpenStackRestAPIException as e: + if httplib.UNAUTHORIZED == e.http_status_code: + response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED + if self._token is not None: + self._token.set_expired() + + else: + DLOG.exception("Caught exception while trying to add " + "router to agent, error=%s." % e) + + except Exception as e: + DLOG.exception("Caught exception while trying to add " + "router_id=%s to agent_id=%s, error=%s." + % (router_id, agent_id, e)) + + finally: + callback.send(response) + callback.close() + + def remove_router_from_agent(self, future, agent_id, router_id, callback): + """ + Remove a router from an L3 Agent. + """ + response = dict() + response['completed'] = False + response['result-data'] = '' + response['reason'] = '' + + try: + future.set_timeouts(config.CONF.get('nfvi-timeouts', None)) + + if self._token is None or \ + self._token.is_expired(): + future.work(openstack.get_token, self._directory) + future.result = (yield) + + if not future.result.is_complete() or \ + future.result.data is None: + DLOG.error("OpenStack get-token did not complete, " + "router_id=%s." % router_id) + return + + self._token = future.result.data + + if self._neutron_extensions is None: + future.work(neutron.get_extensions, self._token) + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-extensions did not complete.") + return + + self._neutron_extensions = future.result.data + + if neutron.lookup_extension(neutron.EXTENSION_NAMES.AGENT, + self._neutron_extensions): + # Send Query request to Neutron + future.work(neutron.remove_router_from_agent, + self._token, agent_id, router_id) + + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron remove-router-from-agent failed, " + "operation did not complete, agent_id=%s " + "router_id=%s" % (agent_id, router_id)) + return + else: + response['result-data'] = future.result.data + else: + DLOG.warn("Neutron Agent Extension not available") + return + + response['completed'] = True + + except exceptions.OpenStackRestAPIException as e: + if httplib.UNAUTHORIZED == e.http_status_code: + response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED + if self._token is not None: + self._token.set_expired() + + else: + DLOG.exception("Caught exception while trying to remove " + "router from agent, error=%s." % e) + + except Exception as e: + DLOG.exception("Caught exception while trying to remove " + "router_id=%s from agent_id=%s, error=%s." + % (router_id, agent_id, e)) + + finally: + callback.send(response) + callback.close() + + def get_physical_network(self, future, network_id, callback): + """ + Get Physical Network of a network. + """ + response = dict() + response['completed'] = False + response['result-data'] = '' + response['reason'] = '' + + try: + future.set_timeouts(config.CONF.get('nfvi-timeouts', None)) + + if self._token is None or \ + self._token.is_expired(): + future.work(openstack.get_token, self._directory) + future.result = (yield) + + if not future.result.is_complete() or \ + future.result.data is None: + DLOG.error("OpenStack get-token did not complete, " + "network_id=%s." % network_id) + return + + self._token = future.result.data + + if self._neutron_extensions is None: + future.work(neutron.get_extensions, self._token) + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-extensions did not complete.") + return + + self._neutron_extensions = future.result.data + + if neutron.lookup_extension(neutron.EXTENSION_NAMES.AGENT, + self._neutron_extensions): + # Send Query request to Neutron + future.work(neutron.get_physical_network, + self._token, network_id) + + future.result = (yield) + + if not future.result.is_complete(): + DLOG.error("Neutron get-physical-network failed, " + "operation did not complete, network_id=%s" + % network_id) + return + else: + response['result-data'] = future.result.data + else: + DLOG.warn("Neutron Agent Extension not available") + return + + response['completed'] = True + + except exceptions.OpenStackRestAPIException as e: + if httplib.UNAUTHORIZED == e.http_status_code: + response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED + if self._token is not None: + self._token.set_expired() + + else: + DLOG.exception("Caught exception while trying to get " + "physical network, error=%s." % e) + + except Exception as e: + DLOG.exception("Caught exception while trying to get %s " + "physical network, error=%s." + % (network_id, e)) + + finally: + callback.send(response) + callback.close() + def query_host_services(self, future, host_uuid, host_name, host_personality, check_fully_up, callback): @@ -1273,6 +1719,7 @@ class NFVINetworkAPI(nfvi.api.v1.NFVINetworkAPI): "operation did not complete, host_uuid=%s, " "host_name=%s." % (host_uuid, host_name)) return + else: if not future.result.data: DLOG.error("Neutron disable-host-services (agents) failed, " diff --git a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/neutron.py b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/neutron.py old mode 100755 new mode 100644 index 2bc9c728..064eb4d0 --- a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/neutron.py +++ b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/neutron.py @@ -77,18 +77,126 @@ AGENT_TYPE = AgentType() VNIC_TYPE = VnicType() -def get_network_agents(token, host_name): +def get_network_agents(token): """ - Retrieve all network agents for a host. + Get Network Agent information for all agents. + """ + url, api_cmd, api_cmd_headers, result_data = _get_network_agents( + token, None) + + return result_data + + +def get_agent_routers(token, agent_id): + """ + Get all routers hosted by a particular agent """ url = token.get_service_url(OPENSTACK_SERVICE.NEUTRON) if url is None: raise ValueError("OpenStack Neutron URL is invalid") - api_cmd = url + "/v2.0/agents?host=" + host_name + api_cmd = url + "/v2.0/agents/" + agent_id + "/l3-routers?fields=id" + + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + + response = rest_api_request(token, "GET", api_cmd, api_cmd_headers) + result_data = response.result_data['routers'] + + return result_data + + +def add_router_to_agent(token, agent_id, router_id): + """ + Schedule a router on an L3 agent + """ + url = token.get_service_url(OPENSTACK_SERVICE.NEUTRON) + if url is None: + raise ValueError("OpenStack Neutron URL is invalid") + + api_cmd = url + "/v2.0/agents/" + agent_id + "/l3-routers" + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + + api_cmd_payload = dict() + api_cmd_payload['router_id'] = router_id + + response = rest_api_request(token, "POST", api_cmd, api_cmd_headers, + json.dumps(api_cmd_payload)) + + return response + + +def remove_router_from_agent(token, agent_id, router_id): + """ + Unschedule a router from an L3 agent + """ + url = token.get_service_url(OPENSTACK_SERVICE.NEUTRON) + if url is None: + raise ValueError("OpenStack Neutron URL is invalid") + + api_cmd = url + "/v2.0/agents/" + agent_id + "/l3-routers/" + router_id + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + + response = rest_api_request(token, "DELETE", api_cmd, api_cmd_headers) + + return response + + +def get_router_ports(token, router_id): + """ + Get port information for particular router + """ + url = token.get_service_url(OPENSTACK_SERVICE.NEUTRON) + if url is None: + raise ValueError("OpenStack Neutron URL is invalid") + + api_cmd = url + "/v2.0/ports?device_id=" + router_id + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + + response = rest_api_request(token, "GET", api_cmd, api_cmd_headers) + + return response + + +def get_physical_network(token, network_id): + """ + Get the physical network of a network + """ + url = token.get_service_url(OPENSTACK_SERVICE.NEUTRON) + if url is None: + raise ValueError("OpenStack Neutron URL is invalid") + + api_cmd = url + "/v2.0/networks/" + network_id + "?fields=provider%3Aphysical_network" + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + + response = rest_api_request(token, "GET", api_cmd, api_cmd_headers) + result_data = response.result_data['network'] + + return result_data + + +def _get_network_agents(token, host_name): + """ + Get network agents of a host + """ + url = token.get_service_url(OPENSTACK_SERVICE.NEUTRON) + if url is None: + raise ValueError("OpenStack Neutron URL is invalid") + + api_cmd = url + "/v2.0/agents" + if host_name is not None: + api_cmd = api_cmd + "?host=" + host_name + else: + # if host_name is None, we are to retrieve information + # on all agents on all hosts. + fields_qualifier = "?fields=id&fields=host&fields=agent_type&fields=alive&fields=admin_state_up" + api_cmd = api_cmd + fields_qualifier api_cmd_headers = dict() - api_cmd_headers['wrs-header'] = 'true' api_cmd_headers['Content-Type'] = "application/json" response = rest_api_request(token, "GET", api_cmd, api_cmd_headers) @@ -452,7 +560,7 @@ def delete_network_agents(token, host_name): Asks OpenStack Neutron to delete agents for a host """ try: - url, api_cmd, api_cmd_headers, result_data = get_network_agents( + url, api_cmd, api_cmd_headers, result_data = _get_network_agents( token, host_name) num_agents_found = 0 @@ -538,7 +646,7 @@ def enable_network_agents(token, host_name): before declaring success. """ try: - url, api_cmd, api_cmd_headers, result_data = get_network_agents( + url, api_cmd, api_cmd_headers, result_data = _get_network_agents( token, host_name) payload = dict() @@ -613,7 +721,7 @@ def disable_network_agents(token, host_name): to False. Other agents are left alone. """ try: - url, api_cmd, api_cmd_headers, result_data = get_network_agents( + url, api_cmd, api_cmd_headers, result_data = _get_network_agents( token, host_name) payload = dict() @@ -700,9 +808,8 @@ def query_network_agents(token, host_name, check_fully_up): Input parameter check_fully_up set to True will check for both alive and admin_state_up, otherwise only alive is checked. """ - url, api_cmd, api_cmd_headers, result_data = get_network_agents( + url, api_cmd, api_cmd_headers, result_data = _get_network_agents( token, host_name) - agent_state = 'up' alive = False admin_state_up = False diff --git a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/sysinv.py b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/sysinv.py index e677ef1f..0894b182 100755 --- a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/sysinv.py +++ b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/sysinv.py @@ -12,6 +12,25 @@ from nfv_plugins.nfvi_plugins.openstack.rest_api import rest_api_request DLOG = debug.debug_get_logger('nfv_plugins.nfvi_plugins.openstack.sysinv') +def get_datanetworks(token, host_uuid): + """ + Get all data networks on a host. + """ + url = token.get_service_url(PLATFORM_SERVICE.SYSINV) + if url is None: + raise ValueError("OpenStack SysInv URL is invalid") + + api_cmd = url + "/ihosts/" + host_uuid + "/interface_datanetworks" + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + api_cmd_headers['User-Agent'] = "vim/1.0" + + response = rest_api_request(token, "GET", api_cmd, api_cmd_headers) + result_data = response.result_data['interface_datanetworks'] + + return result_data + + def get_system_info(token): """ Asks System Inventory for information about the system, such as diff --git a/nfv/nfv-tests/nfv_unit_tests/tests/test_network_rebalance.py b/nfv/nfv-tests/nfv_unit_tests/tests/test_network_rebalance.py new file mode 100644 index 00000000..25c2138e --- /dev/null +++ b/nfv/nfv-tests/nfv_unit_tests/tests/test_network_rebalance.py @@ -0,0 +1,522 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import mock +import random + +from nfv_vim.network_rebalance._network_rebalance import _L3Rebalance +from nfv_vim.network_rebalance._network_rebalance import _reschedule_down_agent +from nfv_vim.network_rebalance._network_rebalance import _reschedule_new_agent +from nfv_vim.network_rebalance._network_rebalance import L3_REBALANCE_STATE + +from . import testcase # noqa: H304 + +DEBUG_PRINTING = False + + +def fake_nfvi_remove_router_from_agent(a, b, c): + pass + + +@mock.patch('nfv_vim.nfvi.nfvi_remove_router_from_agent', fake_nfvi_remove_router_from_agent) +class TestNeutronRebalance(testcase.NFVTestCase): + + def setUp(self): + super(TestNeutronRebalance, self).setUp() + + def tearDown(self): + super(TestNeutronRebalance, self).tearDown() + + def test_rebalance_down_host_canned(self): + _L3Rebalance.reinit() + _L3Rebalance.router_diff_threshold = 1 + + # Down agent will be first agent in list. + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet3'], + 'host': u'compute-0', + 'id': u'00000000-3de6-4717-93d4-0f23c38d2bf2', + 'host_uuid': u'eb2eca67-1018-4c84-9b2c-b9c2662c41a6'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1'], + 'host': u'compute-2', + 'id': u'22222222-5a5f-4c58-9399-12d0b8e7e321', + 'host_uuid': u'021f35d2-4a98-41ab-87c5-2660cecd501d'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1'], + 'host': u'compute-1', + 'id': u'11111111-562c-438c-8083-0733ebbbe881', + 'host_uuid': u'7ebc0819-2b11-4aa8-8ef1-3a5423c17eef'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1', u'physnet3'], + 'host': u'compute-3', + 'id': u'33333333-8989-438c-7083-344322513677', + 'host_uuid': u'23423524-8b11-4ba8-8ef1-2346625326eb'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1'], + 'host': u'compute-5', + 'id': u'55555555-930c-438c-6083-173472902843', + 'host_uuid': u'09132345-7b11-4ca7-8ef1-3a5423c17ecd'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1', u'physnet3'], + 'host': u'compute-4', + 'id': u'44444444-0074-438c-5083-023486659382', + 'host_uuid': u'89891234-3b11-9da8-8ef1-aaa4a3a17aea'}) + + # compute-0 routers + agent_id = u'00000000-3de6-4717-93d4-0f23c38d2bf2' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'415302d1-829c-42ec-aab5-a5b592de5c41') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'fb6c7812-5aa6-4303-a8e8-654d2c61c107') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'f900c5a3-a8f2-4348-a63f-ed0b9d2ca2b1') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'71205e20-d42f-46d0-ad6b-dd325f9b959b') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'43223570-ab32-25d0-ae6c-352aaab23532') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'45692991-e52f-96c0-bd6d-ed428f9a969b') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'97867e20-a92e-1610-a161-1d121f1b151b') + + # compute-2 routers + agent_id = u'22222222-5a5f-4c58-9399-12d0b8e7e321' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'a913c4a3-4d6b-4a4d-9cf5-f8b7c30224a4') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'7c0909c6-c03f-4c14-9d05-e910ab5eb255') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'0c59b77a-b316-4963-90e5-bf689568ac58') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'23423422-3433-fdfd-2222-fdsdfsasvccd') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'11432542-aabb-3415-4443-xcvlkweroidd') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'sd093kds-b2dd-eb3d-23bs-asdwebesdedw') + + # compute-1 routers + agent_id = u'11111111-562c-438c-8083-0733ebbbe881' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'5054adb8-aef5-445d-b335-fc4bb3ee0871') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'91f20f34-ad68-4483-9ae7-8f917a1460d8') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'23093482-bd68-4c83-cae9-9287467ababa') + + # compute-3 routers + agent_id = u'33333333-8989-438c-7083-344322513677' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'51019325-a1d4-410f-a83d-9eb54743dcf0') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'c1c8c935-6302-4c5d-98ee-c12bbd900abf') + + # compute-5 routers + agent_id = u'55555555-930c-438c-6083-173472902843' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'2e50468a-755a-4bfb-bc29-f7aadc66c598') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'7ebc0819-2b11-4aa8-8ef1-3a5423c17eef') + + # compute-4 routers + + agent_id = u'44444444-0074-438c-5083-023486659382' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'4c0213e7-4b36-439b-9e47-d5509e0950f1') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'021f35d2-4a98-41ab-87c5-2660cecd501d') + + _L3Rebalance.networks_per_router[u'415302d1-829c-42ec-aab5-a5b592de5c41'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'fb6c7812-5aa6-4303-a8e8-654d2c61c107'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'f900c5a3-a8f2-4348-a63f-ed0b9d2ca2b1'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'71205e20-d42f-46d0-ad6b-dd325f9b959b'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'43223570-ab32-25d0-ae6c-352aaab23532'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'45692991-e52f-96c0-bd6d-ed428f9a969b'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'97867e20-a92e-1610-a161-1d121f1b151b'] = ['physnet0', 'physnet3'] + + _L3Rebalance.networks_per_router[u'a913c4a3-4d6b-4a4d-9cf5-f8b7c30224a4'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'7c0909c6-c03f-4c14-9d05-e910ab5eb255'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'0c59b77a-b316-4963-90e5-bf689568ac58'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'23423422-3433-fdfd-2222-fdsdfsasvccd'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'11432542-aabb-3415-4443-xcvlkweroidd'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'sd093kds-b2dd-eb3d-23bs-asdwebesdedw'] = ['physnet0', 'physnet1'] + + _L3Rebalance.networks_per_router[u'5054adb8-aef5-445d-b335-fc4bb3ee0871'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'91f20f34-ad68-4483-9ae7-8f917a1460d8'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'23093482-bd68-4c83-cae9-9287467ababa'] = ['physnet0', 'physnet1'] + + _L3Rebalance.networks_per_router[u'51019325-a1d4-410f-a83d-9eb54743dcf0'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'c1c8c935-6302-4c5d-98ee-c12bbd900abf'] = ['physnet0', 'physnet1'] + + _L3Rebalance.networks_per_router[u'2e50468a-755a-4bfb-bc29-f7aadc66c598'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'7ebc0819-2b11-4aa8-8ef1-3a5423c17eef'] = ['physnet0', 'physnet1'] + + _L3Rebalance.networks_per_router[u'4c0213e7-4b36-439b-9e47-d5509e0950f1'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'021f35d2-4a98-41ab-87c5-2660cecd501d'] = ['physnet0', 'physnet1'] + + _L3Rebalance.state_machine_in_progress = False + _L3Rebalance.l3agent_idx = 0 + _L3Rebalance.router_idx = 0 + _L3Rebalance.l3agent_down = '00000000-3de6-4717-93d4-0f23c38d2bf2' + _L3Rebalance.num_routers_on_agents = [7, 6, 3, 2, 2, 2] + _L3Rebalance.num_l3agents = len(_L3Rebalance.num_routers_on_agents) + + _L3Rebalance.set_state(L3_REBALANCE_STATE.RESCHEDULE_DOWN_AGENT) + _L3Rebalance.working_host = 'compute-0' + + while (_L3Rebalance.get_state() == L3_REBALANCE_STATE.RESCHEDULE_DOWN_AGENT): + _reschedule_down_agent() + + # Only agents that can host physnet3 are 3 and 5, expect routers from agent 0 + # to be evenly spread over the two of them. + if DEBUG_PRINTING: + print("_L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + print("_L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + + assert _L3Rebalance.num_routers_on_agents[0] == 0 + assert _L3Rebalance.num_routers_on_agents[1] == 6 + assert _L3Rebalance.num_routers_on_agents[2] == 3 + assert _L3Rebalance.num_routers_on_agents[3] == 6 + assert _L3Rebalance.num_routers_on_agents[4] == 2 + assert _L3Rebalance.num_routers_on_agents[5] == 5 + + def test_rebalance_new_host_canned(self): + _L3Rebalance.reinit() + _L3Rebalance.router_diff_threshold = 1 + + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet3'], + 'host': u'compute-0', + 'id': u'00000000-3de6-4717-93d4-0f23c38d2bf2', + 'host_uuid': u'eb2eca67-1018-4c84-9b2c-b9c2662c41a6'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1'], + 'host': u'compute-2', + 'id': u'22222222-5a5f-4c58-9399-12d0b8e7e321', + 'host_uuid': u'021f35d2-4a98-41ab-87c5-2660cecd501d'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1'], + 'host': u'compute-1', + 'id': u'11111111-562c-438c-8083-0733ebbbe881', + 'host_uuid': u'7ebc0819-2b11-4aa8-8ef1-3a5423c17eef'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1', u'physnet3'], + 'host': u'compute-3', + 'id': u'33333333-8989-438c-7083-344322513677', + 'host_uuid': u'23423524-8b11-4ba8-8ef1-2346625326eb'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1'], + 'host': u'compute-5', + 'id': u'55555555-930c-438c-6083-173472902843', + 'host_uuid': u'09132345-7b11-4ca7-8ef1-3a5423c17ecd'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1', u'physnet3'], + 'host': u'compute-4', + 'id': u'44444444-0074-438c-5083-023486659382', + 'host_uuid': u'89891234-3b11-9da8-8ef1-aaa4a3a17aea'}) + + # compute-0 routers + agent_id = u'00000000-3de6-4717-93d4-0f23c38d2bf2' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'415302d1-829c-42ec-aab5-a5b592de5c41') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'fb6c7812-5aa6-4303-a8e8-654d2c61c107') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'f900c5a3-a8f2-4348-a63f-ed0b9d2ca2b1') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'71205e20-d42f-46d0-ad6b-dd325f9b959b') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'43223570-ab32-25d0-ae6c-352aaab23532') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'45692991-e52f-96c0-bd6d-ed428f9a969b') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'97867e20-a92e-1610-a161-1d121f1b151b') + + # compute-2 routers + agent_id = u'22222222-5a5f-4c58-9399-12d0b8e7e321' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'a913c4a3-4d6b-4a4d-9cf5-f8b7c30224a4') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'7c0909c6-c03f-4c14-9d05-e910ab5eb255') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'0c59b77a-b316-4963-90e5-bf689568ac58') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'23423422-3433-fdfd-2222-fdsdfsasvccd') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'11432542-aabb-3415-4443-xcvlkweroidd') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'sd093kds-b2dd-eb3d-23bs-asdwebesdedw') + + # compute-1 routers + agent_id = u'11111111-562c-438c-8083-0733ebbbe881' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'5054adb8-aef5-445d-b335-fc4bb3ee0871') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'91f20f34-ad68-4483-9ae7-8f917a1460d8') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'23093482-bd68-4c83-cae9-9287467ababa') + + # compute-3 routers + agent_id = u'33333333-8989-438c-7083-344322513677' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'51019325-a1d4-410f-a83d-9eb54743dcf0') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'c1c8c935-6302-4c5d-98ee-c12bbd900abf') + + # compute-5 routers + agent_id = u'55555555-930c-438c-6083-173472902843' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + _L3Rebalance.router_ids_per_agent[agent_id].append(u'4c0213e7-4b36-439b-9e47-d5509e0950f1') + _L3Rebalance.router_ids_per_agent[agent_id].append(u'021f35d2-4a98-41ab-87c5-2660cecd501d') + + # compute-4 routers + agent_id = u'44444444-0074-438c-5083-023486659382' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + + _L3Rebalance.networks_per_router[u'415302d1-829c-42ec-aab5-a5b592de5c41'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'fb6c7812-5aa6-4303-a8e8-654d2c61c107'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'f900c5a3-a8f2-4348-a63f-ed0b9d2ca2b1'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'71205e20-d42f-46d0-ad6b-dd325f9b959b'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'43223570-ab32-25d0-ae6c-352aaab23532'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'45692991-e52f-96c0-bd6d-ed428f9a969b'] = ['physnet0', 'physnet3'] + _L3Rebalance.networks_per_router[u'97867e20-a92e-1610-a161-1d121f1b151b'] = ['physnet0', 'physnet3'] + + _L3Rebalance.networks_per_router[u'a913c4a3-4d6b-4a4d-9cf5-f8b7c30224a4'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'7c0909c6-c03f-4c14-9d05-e910ab5eb255'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'0c59b77a-b316-4963-90e5-bf689568ac58'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'23423422-3433-fdfd-2222-fdsdfsasvccd'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'11432542-aabb-3415-4443-xcvlkweroidd'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'sd093kds-b2dd-eb3d-23bs-asdwebesdedw'] = ['physnet0', 'physnet1'] + + _L3Rebalance.networks_per_router[u'5054adb8-aef5-445d-b335-fc4bb3ee0871'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'91f20f34-ad68-4483-9ae7-8f917a1460d8'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'23093482-bd68-4c83-cae9-9287467ababa'] = ['physnet0', 'physnet1'] + + _L3Rebalance.networks_per_router[u'51019325-a1d4-410f-a83d-9eb54743dcf0'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'c1c8c935-6302-4c5d-98ee-c12bbd900abf'] = ['physnet0', 'physnet1'] + + _L3Rebalance.networks_per_router[u'4c0213e7-4b36-439b-9e47-d5509e0950f1'] = ['physnet0', 'physnet1'] + _L3Rebalance.networks_per_router[u'021f35d2-4a98-41ab-87c5-2660cecd501d'] = ['physnet0', 'physnet1'] + + _L3Rebalance.state_machine_in_progress = False + _L3Rebalance.l3agent_idx = 0 + _L3Rebalance.router_idx = 0 + _L3Rebalance.l3agent_down = '00000000-3de6-4717-93d4-0f23c38d2bf2' + _L3Rebalance.num_routers_on_agents = [7, 6, 3, 2, 2, 0] + _L3Rebalance.num_l3agents = len(_L3Rebalance.num_routers_on_agents) + + _L3Rebalance.set_state(L3_REBALANCE_STATE.RESCHEDULE_NEW_AGENT) + _L3Rebalance.working_host = None + + while (_L3Rebalance.get_state() == L3_REBALANCE_STATE.RESCHEDULE_NEW_AGENT): + _reschedule_new_agent() + + # Only agents that can host physnet3 are 3 and 5, expect routers from agent 0 + # to be evenly spread over the two of them. + if DEBUG_PRINTING: + print("_L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + print("_L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + + assert _L3Rebalance.num_routers_on_agents[0] == 4 + assert _L3Rebalance.num_routers_on_agents[1] == 4 + assert _L3Rebalance.num_routers_on_agents[2] == 3 + assert _L3Rebalance.num_routers_on_agents[3] == 3 + assert _L3Rebalance.num_routers_on_agents[4] == 3 + assert _L3Rebalance.num_routers_on_agents[5] == 3 + + def run_rebalance(self, num_agents_list, network_name_extra, host_name): + _L3Rebalance.reinit() + + _L3Rebalance.l3agents.append({'datanets': ['physnet0', 'physnet1'], + 'host': u'compute-0', + 'id': 'agentid-compute-0', + 'host_uuid': u'eb2eca67-1018-4c84-9b2c-b9c2662c41a6'}) + _L3Rebalance.l3agents.append({'datanets': ['physnet0', 'physnet1'], + 'host': u'compute-1', + 'id': 'agentid-compute-1', + 'host_uuid': u'021f35d2-4a98-41ab-87c5-2660cecd501d'}) + _L3Rebalance.l3agents.append({'datanets': ['physnet0', 'physnet1'], + 'host': u'compute-2', + 'id': 'agentid-compute-2', + 'host_uuid': u'7ebc0819-2b11-4aa8-8ef1-3a5423c17eef'}) + _L3Rebalance.l3agents.append({'datanets': ['physnet0', 'physnet1', network_name_extra], + 'host': u'compute-3', + 'id': 'agentid-compute-3', + 'host_uuid': u'23423524-8b11-4ba8-8ef1-2346625326eb'}) + _L3Rebalance.l3agents.append({'datanets': [u'physnet0', u'physnet1'], + 'host': u'compute-4', + 'id': 'agentid-compute-4', + 'host_uuid': u'09132345-7b11-4ca7-8ef1-3a5423c17ecd'}) + _L3Rebalance.l3agents.append({'datanets': ['physnet0', 'physnet1', network_name_extra], + 'host': u'compute-5', + 'id': 'agentid-compute-5', + 'host_uuid': u'89891234-3b11-9da8-8ef1-aaa4a3a17aea'}) + _L3Rebalance.l3agents.append({'datanets': ['physnet0', 'physnet1', network_name_extra], + 'host': u'compute-6', + 'id': 'agentid-compute-6', + 'host_uuid': u'bbbaaac4-3b21-87a8-65f1-6a3422a11aba'}) + + # compute-0 routers + agent_id = 'agentid-compute-0' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + num_routers = num_agents_list[0] + _L3Rebalance.num_routers_on_agents.append(num_routers) + for router in range(0, num_routers): + _L3Rebalance.router_ids_per_agent[agent_id].append(agent_id + '-' + str(router)) + _L3Rebalance.networks_per_router[agent_id + '-' + str(router)] = ['physnet0', 'physnet1'] + + # compute-1 routers + agent_id = 'agentid-compute-1' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + num_routers = num_agents_list[1] + _L3Rebalance.num_routers_on_agents.append(num_routers) + for router in range(0, num_routers): + _L3Rebalance.router_ids_per_agent[agent_id].append(agent_id + '-' + str(router)) + _L3Rebalance.networks_per_router[agent_id + '-' + str(router)] = ['physnet0', 'physnet1'] + + # compute-2 routers + agent_id = 'agentid-compute-2' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + num_routers = num_agents_list[2] + _L3Rebalance.num_routers_on_agents.append(num_routers) + for router in range(0, num_routers): + _L3Rebalance.router_ids_per_agent[agent_id].append(agent_id + '-' + str(router)) + _L3Rebalance.networks_per_router[agent_id + '-' + str(router)] = ['physnet0', 'physnet1'] + + # compute-3 routers + agent_id = 'agentid-compute-3' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + num_routers = num_agents_list[3] + _L3Rebalance.num_routers_on_agents.append(num_routers) + for router in range(0, num_routers): + _L3Rebalance.router_ids_per_agent[agent_id].append(agent_id + '-' + str(router)) + _L3Rebalance.networks_per_router[agent_id + '-' + str(router)] = ['physnet0', network_name_extra] + + # compute-4 routers + agent_id = 'agentid-compute-4' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + num_routers = num_agents_list[4] + _L3Rebalance.num_routers_on_agents.append(num_routers) + for router in range(0, num_routers): + _L3Rebalance.router_ids_per_agent[agent_id].append(agent_id + '-' + str(router)) + _L3Rebalance.networks_per_router[agent_id + '-' + str(router)] = ['physnet0', 'physnet1'] + + # compute-5 routers + agent_id = 'agentid-compute-5' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + num_routers = num_agents_list[5] + _L3Rebalance.num_routers_on_agents.append(num_routers) + for router in range(0, num_routers): + _L3Rebalance.router_ids_per_agent[agent_id].append(agent_id + '-' + str(router)) + _L3Rebalance.networks_per_router[agent_id + '-' + str(router)] = ['physnet0', 'physnet1', network_name_extra] + + # compute-6 routers + agent_id = 'agentid-compute-6' + _L3Rebalance.router_ids_per_agent[agent_id] = list() + num_routers = num_agents_list[6] + _L3Rebalance.num_routers_on_agents.append(num_routers) + for router in range(0, num_routers): + _L3Rebalance.router_ids_per_agent[agent_id].append(agent_id + '-' + str(router)) + _L3Rebalance.networks_per_router[agent_id + '-' + str(router)] = ['physnet0', 'physnet1', network_name_extra] + + _L3Rebalance.state_machine_in_progress = False + _L3Rebalance.num_l3agents = len(num_agents_list) + + _L3Rebalance.working_host = host_name + if host_name is not None: + _L3Rebalance.set_state(L3_REBALANCE_STATE.RESCHEDULE_DOWN_AGENT) + while (_L3Rebalance.get_state() == L3_REBALANCE_STATE.RESCHEDULE_DOWN_AGENT): + _reschedule_down_agent() + else: + _L3Rebalance.set_state(L3_REBALANCE_STATE.RESCHEDULE_NEW_AGENT) + while (_L3Rebalance.get_state() == L3_REBALANCE_STATE.RESCHEDULE_NEW_AGENT): + _reschedule_new_agent() + + def rebalance(self, host_name=None): + # test not all nets on all agents, expect balancing + # among supported. + num_agents_in = [97, 67, 78, 145, 21, 108, 35] + if DEBUG_PRINTING: + print("host_name = %s, num_agents_in = %s" % (host_name, num_agents_in)) + self.run_rebalance(num_agents_in, 'physnet3', host_name) + assert sum(num_agents_in) == sum(_L3Rebalance.num_routers_on_agents) + + if host_name is not None: + assert _L3Rebalance.num_routers_on_agents[0] == 0 + elif _L3Rebalance.router_diff_threshold == 1: + assert _L3Rebalance.num_routers_on_agents[0] == 66 + assert _L3Rebalance.num_routers_on_agents[1] == 66 + assert _L3Rebalance.num_routers_on_agents[2] == 66 + assert _L3Rebalance.num_routers_on_agents[3] == 96 + assert _L3Rebalance.num_routers_on_agents[4] == 65 + assert _L3Rebalance.num_routers_on_agents[5] == 96 + assert _L3Rebalance.num_routers_on_agents[6] == 96 + # TODO(kevin), make sure each router is only present once. + + if DEBUG_PRINTING: + print("Test 2 _L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + + del num_agents_in[:] + num_agents_in = [5, 20, 31, 32, 44, 0, 0] + if DEBUG_PRINTING: + print("host_name = %s, num_agents_in = %s" % (host_name, num_agents_in)) + self.run_rebalance(num_agents_in, 'physnet3', host_name) + assert sum(num_agents_in) == sum(_L3Rebalance.num_routers_on_agents) + + if DEBUG_PRINTING: + print("Test 2 _L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + # print("Test 2 _L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + + del num_agents_in[:] + num_agents_in = [0, 11, 31, 11, 44, 0, 25] + if DEBUG_PRINTING: + print("host_name = %s, num_agents_in = %s" % (host_name, num_agents_in)) + self.run_rebalance(num_agents_in, 'physnet2', host_name) + assert sum(num_agents_in) == sum(_L3Rebalance.num_routers_on_agents) + + if DEBUG_PRINTING: + print("Test 2 _L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + # print("Test 2 _L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + + del num_agents_in[:] + num_agents_in = [5, 3, 55, 32, 210, 35, 105] + if DEBUG_PRINTING: + print("host_name = %s, num_agents_in = %s" % (host_name, num_agents_in)) + self.run_rebalance(num_agents_in, 'physnet3', host_name) + assert sum(num_agents_in) == sum(_L3Rebalance.num_routers_on_agents) + + if DEBUG_PRINTING: + print("Test 2 _L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + # print("Test 2 _L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + + del num_agents_in[:] + num_agents_in = [0, 0, 5, 0, 0, 0, 0] + if DEBUG_PRINTING: + print("host_name = %s, num_agents_in = %s" % (host_name, num_agents_in)) + self.run_rebalance(num_agents_in, 'physnet2', host_name) + assert sum(num_agents_in) == sum(_L3Rebalance.num_routers_on_agents) + + if DEBUG_PRINTING: + print("Test 2 _L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + # print("Test 2 _L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + + for test in range(0, 50): + del num_agents_in[:] + num_agents_in = [random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150)] + if DEBUG_PRINTING: + print("host_name = %s, num_agents_in = %s" % (host_name, num_agents_in)) + self.run_rebalance(num_agents_in, 'physnet1', host_name) + assert sum(num_agents_in) == sum(_L3Rebalance.num_routers_on_agents) + if DEBUG_PRINTING: + print("Test 2 _L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + + for test in range(0, 50): + del num_agents_in[:] + num_agents_in = [random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150), + random.randint(0, 150)] + if DEBUG_PRINTING: + print("host_name = %s, num_agents_in = %s" % (host_name, num_agents_in)) + self.run_rebalance(num_agents_in, 'physnet3', host_name) + assert sum(num_agents_in) == sum(_L3Rebalance.num_routers_on_agents) + if DEBUG_PRINTING: + print("Test 2 _L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + # print("Test 2 _L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + + def test_rebalance_new_host(self): + _L3Rebalance.router_diff_threshold = 1 + self.rebalance(None) + _L3Rebalance.router_diff_threshold = 2 + self.rebalance(None) + _L3Rebalance.router_diff_threshold = 3 + self.rebalance(None) + _L3Rebalance.router_diff_threshold = 4 + self.rebalance(None) + _L3Rebalance.router_diff_threshold = 5 + self.rebalance(None) + _L3Rebalance.router_diff_threshold = 6 + self.rebalance(None) + + def test_rebalance_down_host(self): + _L3Rebalance.router_diff_threshold = 1 + self.rebalance('compute-0') + _L3Rebalance.router_diff_threshold = 2 + self.rebalance('compute-0') + _L3Rebalance.router_diff_threshold = 3 + self.rebalance('compute-0') + _L3Rebalance.router_diff_threshold = 4 + self.rebalance('compute-0') + _L3Rebalance.router_diff_threshold = 5 + self.rebalance('compute-0') + _L3Rebalance.router_diff_threshold = 6 + self.rebalance('compute-0') diff --git a/nfv/nfv-vim/nfv_vim/config.ini b/nfv/nfv-vim/nfv_vim/config.ini index 31c59aa9..b792851c 100755 --- a/nfv/nfv-vim/nfv_vim/config.ini +++ b/nfv/nfv-vim/nfv_vim/config.ini @@ -45,6 +45,11 @@ max_evacuate_local_image_disk_gb=60 [sw-mgmt-configuration] +[l3agent-rebalance] +timer_interval=1 +router_diff_threshold=3 +hold_off=10 + [vim] rpc_host=127.0.0.1 rpc_port=4343 diff --git a/nfv/nfv-vim/nfv_vim/debug.ini b/nfv/nfv-vim/nfv_vim/debug.ini index 9c78cc79..a3c5d571 100755 --- a/nfv/nfv-vim/nfv_vim/debug.ini +++ b/nfv/nfv-vim/nfv_vim/debug.ini @@ -150,6 +150,7 @@ nfv_vim.strategy.phase: debug.level.info nfv_vim.strategy.stage: debug.level.info nfv_vim.strategy.step: debug.level.info nfv_vim.dor: debug.level.verbose +nfv_vim.l3_rebalance: debug.level.info nfv_vim: debug.level.verbose # ---------------------------------------------------------------------------- nfv_vim.api.openstack: debug.level.verbose diff --git a/nfv/nfv-vim/nfv_vim/host_fsm/_host_task_work.py b/nfv/nfv-vim/nfv_vim/host_fsm/_host_task_work.py index b1513b14..5a9ed20c 100755 --- a/nfv/nfv-vim/nfv_vim/host_fsm/_host_task_work.py +++ b/nfv/nfv-vim/nfv_vim/host_fsm/_host_task_work.py @@ -12,6 +12,7 @@ from nfv_common import timers from nfv_common.helpers import coroutine +from nfv_vim import network_rebalance from nfv_vim import nfvi from nfv_vim.host_fsm._host_defs import HOST_EVENT @@ -178,6 +179,8 @@ class NotifyHostDisabledTaskWork(state_machine.StateTaskWork): """ Callback for notify host disabled """ + from nfv_vim import objects + response = (yield) if self.task is not None: DLOG.verbose("Notify-Host-Disabled callback for %s, response=%s." @@ -186,6 +189,11 @@ class NotifyHostDisabledTaskWork(state_machine.StateTaskWork): self.task.task_work_complete( state_machine.STATE_TASK_WORK_RESULT.SUCCESS, empty_reason) + if (self._host.kubernetes_configured and + (self._service == objects.HOST_SERVICES.NETWORK)): + DLOG.info("Queueing rebalance for host %s disable" % self._host.name) + network_rebalance.add_rebalance_work(self._host.name, True) + else: if self.force_pass: DLOG.info("Notify-Host-Disabled callback for %s, " @@ -193,6 +201,11 @@ class NotifyHostDisabledTaskWork(state_machine.StateTaskWork): self.task.task_work_complete( state_machine.STATE_TASK_WORK_RESULT.SUCCESS, empty_reason) + if (self._host.kubernetes_configured and + (self._service == objects.HOST_SERVICES.NETWORK)): + DLOG.info("Queueing rebalance for host %s disable" % self._host.name) + network_rebalance.add_rebalance_work(self._host.name, True) + else: self.task.task_work_complete( state_machine.STATE_TASK_WORK_RESULT.FAILED, @@ -830,6 +843,11 @@ class EnableHostServicesTaskWork(state_machine.StateTaskWork): self.task.task_work_complete( state_machine.STATE_TASK_WORK_RESULT.SUCCESS, empty_reason) + + if (self._host.kubernetes_configured and + (self._service == objects.HOST_SERVICES.NETWORK)): + DLOG.info("Queueing rebalance for host %s enable" % self._host.name) + network_rebalance.add_rebalance_work(self._host.name, False) else: if self.force_pass: DLOG.info("Enable-Host-Services callback for %s, " @@ -837,6 +855,11 @@ class EnableHostServicesTaskWork(state_machine.StateTaskWork): self.task.task_work_complete( state_machine.STATE_TASK_WORK_RESULT.SUCCESS, empty_reason) + if (self._host.kubernetes_configured and + (self._service == objects.HOST_SERVICES.NETWORK)): + DLOG.info("Queueing rebalance for host %s enable" % self._host.name) + network_rebalance.add_rebalance_work(self._host.name, False) + else: self._host.host_services_update( self._service, diff --git a/nfv/nfv-vim/nfv_vim/network_rebalance/__init__.py b/nfv/nfv-vim/nfv_vim/network_rebalance/__init__.py new file mode 100644 index 00000000..a696e81f --- /dev/null +++ b/nfv/nfv-vim/nfv_vim/network_rebalance/__init__.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015-2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_vim.network_rebalance._network_rebalance import add_rebalance_work # noqa: F401 +from nfv_vim.network_rebalance._network_rebalance import nr_finalize # noqa: F401 +from nfv_vim.network_rebalance._network_rebalance import nr_initialize # noqa: F401 diff --git a/nfv/nfv-vim/nfv_vim/network_rebalance/_network_rebalance.py b/nfv/nfv-vim/nfv_vim/network_rebalance/_network_rebalance.py new file mode 100644 index 00000000..a8314eee --- /dev/null +++ b/nfv/nfv-vim/nfv_vim/network_rebalance/_network_rebalance.py @@ -0,0 +1,990 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common.helpers import Constant +from nfv_common.helpers import Constants +from nfv_common.helpers import coroutine +from nfv_common.helpers import Singleton + +from nfv_common import config +from nfv_common import debug +from nfv_common import timers + +from nfv_vim import nfvi + +DLOG = debug.debug_get_logger('nfv_vim.l3_rebalance') + + +@six.add_metaclass(Singleton) +class AgentType(Constants): + """ + AGENT TYPE Constants + """ + L3 = Constant('L3 agent') + DHCP = Constant('DHCP agent') + +AGENT_TYPE = AgentType() + + +@six.add_metaclass(Singleton) +class L3RebalanceState(Constants): + """ + L3 REBALANCE STATE Constants + """ + GET_NETWORK_AGENTS = Constant('GET_NETWORK_AGENTS') + GET_ROUTERS_HOSTED_ON_AGENT = Constant('GET_ROUTERS_HOSTED_ON_AGENT') + GET_ROUTER_PORT_NETWORKS = Constant('GET_ROUTER_PORT_NETWORKS') + GET_PHYSICAL_NETWORK_FROM_NETWORKS = Constant('GET_PHYSICAL_NETWORK_FROM_NETWORKS') + GET_HOST_PHYSICAL_NETWORKS = Constant('GET_HOST_PHYSICAL_NETWORKS') + RESCHEDULE_DOWN_AGENT = Constant('RESCHEDULE_DOWN_AGENT') + RESCHEDULE_NEW_AGENT = Constant('RESCHEDULE_NEW_AGENT') + HOLD_OFF = Constant('HOLD_OFF') + DONE = Constant('DONE') + +L3_REBALANCE_STATE = L3RebalanceState() + + +@six.add_metaclass(Singleton) +class L3AgentRebalance(object): + def __init__(self): + # Our state. + self.state = L3_REBALANCE_STATE.DONE + # If rebalance occurring due to down agent, + # entry zero in below list will be for the down agent + # list of dictionaries of agent information. + self.l3agents = list() + # Dictionary based on agent_id of router ids hosted + # on an agent. + self.router_ids_per_agent = dict() + # For keeping track of routers that cant be schedule. + # Useful for logging and test. + self.router_ids_per_agent_cant_schedule = dict() + # Dictionary based on router_id of list of physical + # networks of ports on router. + self.networks_per_router = dict() + # For determining whether state machine work is finished + # in a tick and the state machine can progress. + self.state_machine_in_progress = False + # Indexes into the various data structures. + self.l3agent_idx = 0 + self.num_l3agents = 0 + self.router_idx = 0 + self.num_routers = 0 + self.net_idx = 0 + # If rebalance occurring due to down agent, + # entry zero in below list will be for the down agent + self.num_routers_on_agents = list() + # The queue of work that is to be processed + self.work_queue = list() + # The difference between maximum routers on an agent + # and minimum routers on an agent we are trying to achieve. + self.router_diff_threshold = 1 + # List of (index, number of routers on agents) tuples + # for all agents. + self.agent_list = list() + # For rebalance, below will be None, as we don't actually + # care about the name of a new host who's agent has just + # come up. For agent down, it will be the name of the host + # going down. + self.working_host = None + # Number of ticks to wait after seeing work to begin work. + self.hold_off = 3 + # Current number of ticks waiting to begin work. + self.current_hold_off_count = 0 + # queues that maintain host names of hosts coming up and going down. + self.host_up_queue = list() + self.host_down_queue = list() + + def reinit(self): + self.num_l3agents = 0 + self.l3agent_idx = 0 + del self.l3agents[:] + self.router_ids_per_agent = {} + self.router_ids_per_agent_cant_schedule = {} + self.networks_per_router = {} + del self.num_routers_on_agents[:] + + def add_agent(self, agent_id): + self.router_ids_per_agent[agent_id] = list() + self.num_l3agents += 1 + + def get_current_l3agent(self): + agent_id = self.l3agents[self.l3agent_idx]['id'] + host_name = self.l3agents[self.l3agent_idx]['host'] + return agent_id, host_name + + def update_current_l3agent(self, key, value): + self.l3agents[self.l3agent_idx][key] = value + + def add_router_to_agent(self, agent_id, router_id): + self.router_ids_per_agent[agent_id].append(router_id) + + def agent_routers_done(self): + agent_id = self.l3agents[self.l3agent_idx]['id'] + _L3Rebalance.num_routers_on_agents.append( + len(_L3Rebalance.router_ids_per_agent[agent_id])) + self.l3agent_idx += 1 + return self.l3agent_idx == self.num_l3agents + + def add_network_to_router(self, router_to_resched, network_id): + self.networks_per_router[router_to_resched].append(network_id) + + def router_ports_done(self): + self.router_idx += 1 + DLOG.debug("router_idx = %s, l3agent_idx = %s, num_routers= %s" % + (self.router_idx, self.l3agent_idx, self.num_routers)) + + if self.router_idx >= self.num_routers: + # We have router port info for all routers on this agent + # move on to next one. + self.router_idx = 0 + self.l3agent_idx += 1 + if (((self.working_host is not None) and (self.l3agent_idx == 1)) or + (self.l3agent_idx == self.num_l3agents)): + # We have router port info for all routers on all agents + # that we care about. Get the Physical Network info for these. + return True + + DLOG.debug("self.networks_per_router = %s" % self.networks_per_router) + return False + + def get_current_working_router(self): + agent_routers = self.router_ids_per_agent[self.l3agents[self.l3agent_idx]['id']] + self.num_routers = len(agent_routers) + if self.num_routers > 0: + working_router = agent_routers[self.router_idx] + self.networks_per_router[working_router] = list() + return working_router + else: + return None + + def get_current_working_network(self): + agent_routers = self.router_ids_per_agent[self.l3agents[self.l3agent_idx]['id']] + self.num_routers = len(agent_routers) + if self.num_routers > 0: + working_router = agent_routers[self.router_idx] + working_network = self.networks_per_router[working_router][self.net_idx] + return working_network + else: + return None + + def current_working_network_advance_agent(self): + self.l3agent_idx += 1 + + def get_host_id_of_current_l3agent(self): + return self.l3agents[self.l3agent_idx]['host_uuid'] + + def update_network(self, physical_network): + """ + Overwrite the network_id of the stored network with the input + physical_network. + Returns True if there are no more networks to gather, False otherwise + """ + agent_routers = self.router_ids_per_agent[self.l3agents[self.l3agent_idx]['id']] + working_router = agent_routers[self.router_idx] + # overwrite the netid with that of the physical network. + self.networks_per_router[working_router][self.net_idx] = physical_network + self.net_idx += 1 + + if self.net_idx == len(self.networks_per_router[working_router]): + self.net_idx = 0 + self.router_idx += 1 + if self.router_idx >= len(agent_routers): + self.router_idx = 0 + self.l3agent_idx += 1 + if (self.l3agent_idx >= self.num_l3agents) or self.get_working_host(): + return True + + # Iterate until we find an agent with routers, or until we've run out of agents + while (len(self.router_ids_per_agent[self.l3agents[self.l3agent_idx]['id']]) == 0): + self.l3agent_idx += 1 + if (self.l3agent_idx >= self.num_l3agents): + return True + + return False + + def update_datanetworks(self, datanetwork_name): + if not self.l3agents[self.l3agent_idx].get('datanets', False): + self.l3agents[self.l3agent_idx]['datanets'] = list() + self.l3agents[self.l3agent_idx]['datanets'].append(datanetwork_name) + + def datanetworks_done(self): + self.l3agent_idx += 1 + if self.l3agent_idx == self.num_l3agents: + return True + else: + return False + + def get_next_router_to_move(self): + # If there are any routers on the down agent, then + # return the next router and its networks. + agent_id = self.get_down_agent_id() + if len(self.router_ids_per_agent[agent_id]) > 0: + router_to_move = self.router_ids_per_agent[agent_id][0] + router_to_move_physical_networks = self.networks_per_router[router_to_move] + return router_to_move, router_to_move_physical_networks + else: + return None, None + + def find_router_with_physical_networks(self, + agent_idx, + physical_networks): + agent_routers = self.router_ids_per_agent[self.l3agents[agent_idx]['id']] + + all_networks_found = False + router_id = None + for router_id in agent_routers: + + router_networks = self.networks_per_router[router_id] + DLOG.debug("router_networks = %s, physical_networks = %s" % (router_networks, physical_networks)) + all_networks_found = True + for network in router_networks: + if network not in physical_networks: + all_networks_found = False + break + + if all_networks_found: + break + + if all_networks_found: + return router_id + else: + # we couldn't find a router with networks matching the requirements + return None + + def populate_l3agents(self, result_data): + for agent in result_data: + if agent['agent_type'] == AGENT_TYPE.L3: + agent_info_dict = dict() + agent_info_dict['host'] = agent['host'] + agent_info_dict['id'] = agent['id'] + # For simplicity and easy of access, place the down host + # (if applicable) first in the list. + if agent['host'] == self.get_working_host(): + self.l3agents.insert(0, agent_info_dict) + elif agent['alive'] and agent['admin_state_up']: + self.l3agents.append(agent_info_dict) + self.add_agent(agent['id']) + + return len(self.l3agents) + + def get_down_agent_id(self): + return self.l3agents[0]['id'] + + def get_agent_id_from_index(self, agent_index): + return self.l3agents[agent_index]['id'] + + def get_num_routers_on_agents(self): + return self.num_routers_on_agents + + def get_host_physical_networks(self, agent_index): + return self.l3agents[agent_index]['datanets'] + + def move_agent_router(self, router_to_move, source_agent, target_agent): + target_agent_id = _L3Rebalance.get_agent_id_from_index(target_agent) + source_agent_id = _L3Rebalance.get_agent_id_from_index(source_agent) + + _L3Rebalance.num_routers_on_agents[target_agent] += 1 + _L3Rebalance.num_routers_on_agents[source_agent] -= 1 + + self.router_ids_per_agent[source_agent_id].remove(router_to_move) + self.router_ids_per_agent[target_agent_id].append(router_to_move) + + def move_agent_router_to_cant_schedule(self, router_to_move, agent_index): + source_agent_id = _L3Rebalance.get_agent_id_from_index(agent_index) + _L3Rebalance.num_routers_on_agents[agent_index] -= 1 + + self.router_ids_per_agent[source_agent_id].remove(router_to_move) + if self.router_ids_per_agent_cant_schedule.get(source_agent_id, None) is None: + self.router_ids_per_agent_cant_schedule[source_agent_id] = list() + + self.router_ids_per_agent_cant_schedule[source_agent_id].append(router_to_move) + + def get_working_host(self): + # working_host will be None if we are doing a rebalance + # due to a new l3 agent becoming available. + return self.working_host + + def set_working_host(self, host_name=None): + # working_host will be None if we are doing a rebalance + # due to a new l3 agent becoming available. + self.working_host = host_name + + def routers_are_balanced(self): + + possible_agent_targets = range(0, len(self.num_routers_on_agents)) + + # find the agent with the least amount of routers. + agent_with_least_routers = min(possible_agent_targets, + key=self.num_routers_on_agents.__getitem__) + min_routers = self.num_routers_on_agents[agent_with_least_routers] + + agent_with_most_routers = max(possible_agent_targets, + key=self.num_routers_on_agents.__getitem__) + max_routers = self.num_routers_on_agents[agent_with_most_routers] + + if ((max_routers - min_routers) <= _L3Rebalance.router_diff_threshold): + DLOG.debug("max:%s - min:%s <= DIFF, balanced" % (max_routers, min_routers)) + return True + + return False + + def no_routers_on_down_host(self): + return self.num_routers_on_agents[0] == 0 + + def remove_agent(self, agent_with_most_routers): + _L3Rebalance.num_routers_on_agents.remove(agent_with_most_routers) + _L3Rebalance.l3agents.remove(agent_with_most_routers) + + def set_state(self, state): + # Set up state for next tick. + self.state = state + self.router_idx = 0 + self.l3agent_idx = 0 + self.net_idx = 0 + self.state_machine_in_progress = False + if ((state == L3_REBALANCE_STATE.RESCHEDULE_DOWN_AGENT) or + (state == L3_REBALANCE_STATE.RESCHEDULE_NEW_AGENT)): + self.create_agent_list() + + elif state == L3_REBALANCE_STATE.DONE: + self.debug_dump() + elif state == L3_REBALANCE_STATE.HOLD_OFF: + self.current_hold_off_count = 0 + + def get_state(self): + return self.state + + def add_rebalance_work(self, host_name, host_is_going_down): + if host_is_going_down: + self.host_down_queue.append(host_name) + else: + self.host_up_queue.append(host_name) + + def create_agent_list(self): + del self.agent_list[:] + for index, agent in enumerate(self.l3agents): + agent_list_entry = (index, self.num_routers_on_agents[index]) + self.agent_list.append(agent_list_entry) + + def get_min_agent_list_data(self): + agent_with_least_routers_entry = min(self.agent_list, key=lambda t: t[1]) + return agent_with_least_routers_entry[0], agent_with_least_routers_entry[1] + + def get_max_agent_list_data(self): + agent_with_most_routers_entry = max(self.agent_list, key=lambda t: t[1]) + return agent_with_most_routers_entry[0], agent_with_most_routers_entry[1] + + def get_agent_list_scheduling_info(self): + possible_agent_targets = list() + num_routers_on_agents = list() + for entry in self.agent_list: + possible_agent_targets.append(entry[0]) + num_routers_on_agents.append(entry[1]) + + return num_routers_on_agents, possible_agent_targets + + def agent_list_remove(self, agent_list_tuple): + self.agent_list.remove(agent_list_tuple) + + def agent_list_increment(self, agent_index): + for idx, val in enumerate(self.agent_list): + if val[0] == agent_index: + self.agent_list[idx] = (val[0], val[1] + 1) + break + + def agent_list_decrement(self, agent_index): + for idx, val in enumerate(self.agent_list): + if val[0] == agent_index: + self.agent_list[idx] = (val[0], val[1] - 1) + break + + def hold_off_is_done(self): + self.current_hold_off_count += 1 + return self.current_hold_off_count >= self.hold_off + + def debug_dump(self): + DLOG.debug("_L3Rebalance.l3agents = %s" % _L3Rebalance.l3agents) + DLOG.debug("_L3Rebalance.router_ids_per_agent= %s" % _L3Rebalance.router_ids_per_agent) + DLOG.debug("_L3Rebalance.networks_per_router= %s" % _L3Rebalance.networks_per_router) + DLOG.debug("_L3Rebalance.state_machine_in_progress= %s" % _L3Rebalance.state_machine_in_progress) + DLOG.debug("_L3Rebalance.l3agent_idx= %s" % _L3Rebalance.l3agent_idx) + DLOG.debug("_L3Rebalance.num_l3agents= %s" % _L3Rebalance.num_l3agents) + DLOG.debug("_L3Rebalance.router_idx= %s" % _L3Rebalance.router_idx) + DLOG.debug("_L3Rebalance.num_routers_on_agents= %s" % _L3Rebalance.num_routers_on_agents) + + +_L3Rebalance = L3AgentRebalance() + + +def add_rebalance_work(host_name, host_is_going_down): + """ + API for external use to launch a rebalance operation. + host_is_going_down is boolean indicating if the host is + coming up (rebalance routers, moving some to newly available host), + or going down (move routers off this host, distributing amongst rest) + """ + global _L3Rebalance + + _L3Rebalance.add_rebalance_work(host_name, host_is_going_down) + + +@coroutine +def _add_router_to_agent_callback(): + """ + Add router to agent callback + """ + global _L3Rebalance + + response = (yield) + + _L3Rebalance.state_machine_in_progress = False + DLOG.debug("_add_router_to_agent_callback, response = %s" % response) + if not response['completed']: + # Nothing we can really do except log this and resume our state machine.. + DLOG.warn("Unable to add router to l3 agent, response = %s" % response) + + +@coroutine +def _remove_router_from_agent_callback(to_agent_id, router_id): + """ + Remove router from agent callback + """ + global _L3Rebalance + + response = (yield) + + DLOG.debug("_remove_router_from_agent_callback , response = %s" % response) + if response['completed']: + # After successfully detaching router from agent, attach + # to target agent. + nfvi.nfvi_add_router_to_agent(to_agent_id, router_id, _add_router_to_agent_callback()) + else: + # Couldn't detach the router, no sense trying to attach. + # Just resume state machine. + _L3Rebalance.state_machine_in_progress = False + DLOG.warn("Unable to remove router from l3 agent, response = %s" % response) + + +@coroutine +def _get_datanetworks_callback(host_id): + """ + Get data networks callback + """ + global _L3Rebalance + + response = (yield) + + _L3Rebalance.state_machine_in_progress = False + DLOG.debug("_get_datanetworks, response = %s" % response) + if response['completed']: + result_data = response.get('result-data', None) + for data_net in result_data: + _L3Rebalance.update_datanetworks(data_net['datanetwork_name']) + + if _L3Rebalance.datanetworks_done(): + # Make the choice of which state to enter here + if _L3Rebalance.get_working_host() is not None: + _L3Rebalance.set_state(L3_REBALANCE_STATE.RESCHEDULE_DOWN_AGENT) + else: + _L3Rebalance.set_state(L3_REBALANCE_STATE.RESCHEDULE_NEW_AGENT) + else: + DLOG.error("Unable to retrieve data networks for host: %s" % host_id) + # TODO(KSMITH) Is this error recoverable? For now, abort. + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +def _get_host_data_networks(): + """ + Get the physical networks supported by a host. + """ + + host_id = _L3Rebalance.get_host_id_of_current_l3agent() + nfvi.nfvi_get_datanetworks(host_id, _get_datanetworks_callback(host_id)) + + +@coroutine +def _get_physical_network_callback(network_id): + """ + Get Physical Network callback + """ + global _L3Rebalance + + response = (yield) + + _L3Rebalance.state_machine_in_progress = False + DLOG.debug("_get_physical_network_callback, response = %s" % response) + if response['completed']: + result_data = response.get('result-data', None) + if _L3Rebalance.update_network(result_data['provider:physical_network']): + _L3Rebalance.set_state(L3_REBALANCE_STATE.GET_HOST_PHYSICAL_NETWORKS) + else: + DLOG.error("Unable to get physical network for network: %s" % network_id) + # TODO(KSMITH) Is this error recoverable? For now, abort. + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +def _get_physical_networks(): + """ + Get the physical network corresponding to a network. + """ + + network_id = _L3Rebalance.get_current_working_network() + DLOG.debug("Current working network: %s" % network_id) + if network_id is not None: + nfvi.nfvi_get_physical_network(network_id, _get_physical_network_callback(network_id)) + else: + # We get here if there are no routers on this agent, + # Stay in same state, but advance to next agent + _L3Rebalance.current_working_network_advance_agent() + _L3Rebalance.state_machine_in_progress = False + + +@coroutine +def _get_router_ports_callback(router): + """ + Get Router Ports callback. Save the network_id for each port attached + to the router. + """ + global _L3Rebalance + + response = (yield) + + _L3Rebalance.state_machine_in_progress = False + DLOG.debug("_get_router_ports_callback, response = %s" % response) + if response['completed']: + result_data = response.get('result-data', None) + for port in result_data['ports']: + network_id = port['network_id'] + _L3Rebalance.add_network_to_router(router, network_id) + + if _L3Rebalance.router_ports_done(): + # we're done getting routers for this agent. + _L3Rebalance.set_state(L3_REBALANCE_STATE.GET_PHYSICAL_NETWORK_FROM_NETWORKS) + + DLOG.debug("_L3Rebalance.networks_per_router = %s" % _L3Rebalance.networks_per_router) + else: + DLOG.error("Unable to get ports for router: %s" % router) + # TODO(KSMITH) Is this error recoverable? For now, abort. + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +def _get_router_port_networks(): + """ + For each router, look at all the ports and find the + underlying physical network. Even though pagination is supported, + do not worry about it as the assumption is that there will be a + relatively small number of ports on the router. + """ + global _L3Rebalance + + router = _L3Rebalance.get_current_working_router() + + if router is not None: + nfvi.nfvi_get_router_ports(router, _get_router_ports_callback(router)) + elif _L3Rebalance.router_ports_done(): + # we're done getting routers port networks, + # advance to next state + _L3Rebalance.set_state(L3_REBALANCE_STATE.GET_PHYSICAL_NETWORK_FROM_NETWORKS) + else: + # We get here if there are no routers on this agent, + # Stay in same state, but advance to next agent + _L3Rebalance.state_machine_in_progress = False + + +@coroutine +def _get_agent_routers_callback(agent_id): + """ + Get Agent Routers callback + """ + global _L3Rebalance + + response = (yield) + + _L3Rebalance.state_machine_in_progress = False + DLOG.debug("_get_agent_routers_callback, response = %s" % response) + if response['completed']: + + result_data = response.get('result-data', None) + for router in result_data: + _L3Rebalance.add_router_to_agent(agent_id, router['id']) + + DLOG.debug("_L3Rebalance.l3agent_idx = %s, _L3Rebalance.num_l3agents = %s" % + (_L3Rebalance.l3agent_idx, _L3Rebalance.num_l3agents)) + + if _L3Rebalance.agent_routers_done(): + + _L3Rebalance.set_state(L3_REBALANCE_STATE.GET_ROUTER_PORT_NETWORKS) + + # Do this check here to save us from going through the rest + # of the state machine + if _L3Rebalance.get_working_host() is None: + if _L3Rebalance.routers_are_balanced(): + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + return + + if _L3Rebalance.get_working_host() is not None: + if _L3Rebalance.no_routers_on_down_host(): + # Check to see if there are no routers on the + # down host in the first place. + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + else: + DLOG.error("Could not get routers on agent: %s" % agent_id) + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +def _get_routers_on_agents(): + """ + Get Routers hosted by an L3 Agent + Note paging is not supported by the l3-agent api. + """ + + from nfv_vim import tables + global _L3Rebalance + + # Agent of interest is first in the list. + # In the case of an agent going down, this will be important + + agent_id, host_name = _L3Rebalance.get_current_l3agent() + + host_table = tables.tables_get_host_table() + host = host_table.get(host_name, None) + + if host is not None: + _L3Rebalance.update_current_l3agent('host_uuid', host.uuid) + else: + DLOG.error("Cannot find rebalance host: %s" % host_name) + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + nfvi.nfvi_get_agent_routers(agent_id, _get_agent_routers_callback(agent_id)) + + +@coroutine +def _get_network_agents_callback(): + """ + Get Network Agents callback + """ + global _L3Rebalance + + response = (yield) + + _L3Rebalance.state_machine_in_progress = False + DLOG.debug("_get_network_agents_callback, response = %s" % response) + if response['completed']: + result_data = response.get('result-data', None) + + num_agents = _L3Rebalance.populate_l3agents(result_data) + + if num_agents < 2: + # no sense doing anything if less than 2 agents + DLOG.debug("Less than 2 l3agents, no rebalancing required") + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + else: + _L3Rebalance.set_state(L3_REBALANCE_STATE.GET_ROUTERS_HOSTED_ON_AGENT) + + else: + DLOG.error("Failed to get network agents, aborting l3 agent rebalance") + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +def _get_network_agents(): + """ + Get Network Agents + Note paging is not supported for getting network agents. + """ + global _L3Rebalance + + nfvi.nfvi_get_network_agents(_get_network_agents_callback()) + + +def _reschedule_down_agent(): + """ + Reschedule down agent + """ + + # For each Router on the down agent, schedule it to the host with the + # least amount of routers that also hosts the required physical networks. + + global _L3Rebalance + + found_router_to_move = False + router_to_move = '' + + num_routers_on_agents, possible_agent_targets = \ + _L3Rebalance.get_agent_list_scheduling_info() + + # Remove the agent going down from consideration. + possible_agent_targets.pop(0) + num_routers_on_agents.pop(0) + + while not found_router_to_move: + + router_to_move, router_to_move_physical_networks = \ + _L3Rebalance.get_next_router_to_move() + + if router_to_move is None: + # we're done... + break + + agent_with_least_routers = 0 + + while len(possible_agent_targets) > 0: + + min_routers = min(num_routers_on_agents) + + agent_with_least_routers_index = num_routers_on_agents.index(min_routers) + agent_with_least_routers = possible_agent_targets[agent_with_least_routers_index] + + host_physical_networks = _L3Rebalance.get_host_physical_networks(agent_with_least_routers) + + # Does the host of this agent have the needed physical networks? + target_good = True + for network in router_to_move_physical_networks: + if network not in host_physical_networks: + target_good = False + break + + if not target_good: + # Check next agent/host + possible_agent_targets.pop(agent_with_least_routers_index) + num_routers_on_agents.pop(agent_with_least_routers_index) + else: + # This target agent/host is good. + break + + if len(possible_agent_targets) == 0: + _L3Rebalance.move_agent_router_to_cant_schedule(router_to_move, 0) + DLOG.debug("Unable to reschedule router, no valid target found") + found_router_to_move = False + else: + found_router_to_move = True + _L3Rebalance.move_agent_router(router_to_move, 0, agent_with_least_routers) + + _L3Rebalance.agent_list_increment(agent_with_least_routers) + _L3Rebalance.agent_list_decrement(0) + + source_agent_id = _L3Rebalance.get_agent_id_from_index(0) + target_agent_id = _L3Rebalance.get_agent_id_from_index(agent_with_least_routers) + DLOG.debug("Rescheduling router:%s to agent: %s" % (router_to_move, target_agent_id)) + nfvi.nfvi_remove_router_from_agent(source_agent_id, + router_to_move, + _remove_router_from_agent_callback( + target_agent_id, + router_to_move)) + + if not found_router_to_move: + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +def _reschedule_new_agent(): + """ + Reschedule for a new agent coming up. + Try to achieve a balance of routers hosted by the L3 agents. + """ + global _L3Rebalance + + agent_with_least_routers, min_routers = _L3Rebalance.get_min_agent_list_data() + agent_with_most_routers, max_routers = _L3Rebalance.get_max_agent_list_data() + + if (max_routers - min_routers) <= _L3Rebalance.router_diff_threshold: + DLOG.debug("Threshold exit") + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + return + + num_routers_on_agents = list() + possible_agent_targets = list() + num_routers_on_agents, possible_agent_targets = \ + _L3Rebalance.get_agent_list_scheduling_info() + + # Remove our current max router agent from consideration. + agent_with_most_routers_index = possible_agent_targets.index(agent_with_most_routers) + possible_agent_targets.pop(agent_with_most_routers_index) + num_routers_on_agents.pop(agent_with_most_routers_index) + + while (True): + + min_routers = min(num_routers_on_agents) + + agent_with_least_routers_index = num_routers_on_agents.index(min_routers) + agent_with_least_routers = possible_agent_targets[agent_with_least_routers_index] + + host_physical_networks = _L3Rebalance.get_host_physical_networks( + agent_with_least_routers) + + # find a router on the agent with most routers that has ports + # on the physical networks of the agent with least routers. + router_to_move = _L3Rebalance.find_router_with_physical_networks( + agent_with_most_routers, + host_physical_networks) + + if router_to_move is None: + # Couldn't find a match, eliminate the current least router agent + # as a candidate. + DLOG.debug("Could not find a router to move to agent %s" % agent_with_least_routers) + agent_with_least_routers_index = possible_agent_targets.index(agent_with_least_routers) + possible_agent_targets.pop(agent_with_least_routers_index) + num_routers_on_agents.pop(agent_with_least_routers_index) + + if len(possible_agent_targets) == 0: + # no more agents left to try, we can't move any routers off + # the current max router agent. Remove it from consideration. + DLOG.debug("No more agents to try for max router agent") + + _L3Rebalance.agent_list_remove((agent_with_most_routers, max_routers)) + # keep same state so we will come back, clear the below flag as no callback + # will do it for us. + _L3Rebalance.state_machine_in_progress = False + return + + else: + # before we move this router, it is possible that due to incompatible networks, + # we now are looking at an agent that doesn't meet our threshold requirements + # if that is the case, do not move the router. We are done trying to move + # routers off this agent + if (max_routers - min_routers) <= _L3Rebalance.router_diff_threshold: + DLOG.debug("No more agents to try for max router agent " + "and threshold not met, cannot balance.") + _L3Rebalance.agent_list_remove((agent_with_most_routers, max_routers)) + # clear the below flag as no callback will do it for us. + _L3Rebalance.state_machine_in_progress = False + return + + _L3Rebalance.move_agent_router(router_to_move, + agent_with_most_routers, + agent_with_least_routers) + + _L3Rebalance.agent_list_increment(agent_with_least_routers) + _L3Rebalance.agent_list_decrement(agent_with_most_routers) + + source_agent_id = _L3Rebalance.get_agent_id_from_index(agent_with_most_routers) + target_agent_id = _L3Rebalance.get_agent_id_from_index(agent_with_least_routers) + + DLOG.debug("Rescheduling router:%s from agent: %s to agent: %s" % + (router_to_move, source_agent_id, target_agent_id)) + nfvi.nfvi_remove_router_from_agent(source_agent_id, + router_to_move, + _remove_router_from_agent_callback( + target_agent_id, + router_to_move)) + + return + + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +@coroutine +def _nr_timer(): + """ + Network Rebalance timer + """ + global _L3Rebalance + + while True: + (yield) + + if not _L3Rebalance.state_machine_in_progress: + + _L3Rebalance.state_machine_in_progress = True + + my_state = _L3Rebalance.get_state() + DLOG.debug("Network Rebalance State %s" % my_state) + if my_state == L3_REBALANCE_STATE.GET_NETWORK_AGENTS: + + _L3Rebalance.reinit() + _get_network_agents() + + elif my_state == L3_REBALANCE_STATE.GET_ROUTERS_HOSTED_ON_AGENT: + + _get_routers_on_agents() + + elif my_state == L3_REBALANCE_STATE.GET_ROUTER_PORT_NETWORKS: + + _get_router_port_networks() + + elif my_state == L3_REBALANCE_STATE.GET_PHYSICAL_NETWORK_FROM_NETWORKS: + + _get_physical_networks() + + elif my_state == L3_REBALANCE_STATE.GET_HOST_PHYSICAL_NETWORKS: + + _get_host_data_networks() + + elif my_state == L3_REBALANCE_STATE.RESCHEDULE_DOWN_AGENT: + + _reschedule_down_agent() + + elif my_state == L3_REBALANCE_STATE.RESCHEDULE_NEW_AGENT: + + _reschedule_new_agent() + + elif my_state == L3_REBALANCE_STATE.DONE: + + _L3Rebalance.state_machine_in_progress = False + + # Check for work... + if ((len(_L3Rebalance.host_up_queue) > 0) or + (len(_L3Rebalance.host_down_queue) > 0)): + _L3Rebalance.set_state(L3_REBALANCE_STATE.HOLD_OFF) + + elif my_state == L3_REBALANCE_STATE.HOLD_OFF: + + _L3Rebalance.state_machine_in_progress = False + if _L3Rebalance.hold_off_is_done(): + if len(_L3Rebalance.host_down_queue) > 0: + # A reschedule for every down host is required. + # Do the down hosts rescheduling before handling + # the up hosts, as if both are pending, we don't + # want to move routers to agents that are about to + # go down. + down_host = _L3Rebalance.host_down_queue.pop(0) + _L3Rebalance.set_working_host(down_host) + DLOG.info("Triggering L3 Agent reschedule for " + "disabled l3 agent host: %s" % + down_host) + elif len(_L3Rebalance.host_up_queue) > 0: + # Even if multiple hosts come up, we only need to + # reschedule once + _L3Rebalance.set_working_host(None) + DLOG.info("Triggering L3 Agent reschedule for " + "enabled l3 agent host(s): %s" % + _L3Rebalance.host_up_queue) + del _L3Rebalance.host_up_queue[:] + + _L3Rebalance.set_state(L3_REBALANCE_STATE.GET_NETWORK_AGENTS) + + else: + DLOG.error("Unknown state: %s, resetting" % my_state) + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + +def nr_initialize(): + """ + Initialize Network Rebalance handling + """ + global _L3Rebalance + + _L3Rebalance.set_state(L3_REBALANCE_STATE.DONE) + + if config.section_exists('l3agent-rebalance'): + section = config.CONF['l3agent-rebalance'] + _nr_timer_interval = int(section.get('timer_interval', 10)) + _L3Rebalance.router_diff_threshold = int(section.get('router_diff_threshold', 3)) + _L3Rebalance.hold_off = int(section.get('hold_off', 3)) + else: + _nr_timer_interval = 10 + _L3Rebalance.router_diff_threshold = 3 + _L3Rebalance.hold_off = 3 + + timers.timers_create_timer('nr', 1, _nr_timer_interval, _nr_timer) + + +def nr_finalize(): + """ + Finalize Network Rebalance handling + """ + pass diff --git a/nfv/nfv-vim/nfv_vim/nfvi/__init__.py b/nfv/nfv-vim/nfv_vim/nfvi/__init__.py index fb7c2749..f0f0abbe 100755 --- a/nfv/nfv-vim/nfv_vim/nfvi/__init__.py +++ b/nfv/nfv-vim/nfv_vim/nfvi/__init__.py @@ -91,6 +91,7 @@ from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_disable_container_host from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_enable_container_host_services # noqa: F401 from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_get_alarm_history # noqa: F401 from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_get_alarms # noqa: F401 +from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_get_datanetworks # noqa: F401 from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_get_host # noqa: F401 from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_get_hosts # noqa: F401 from nfv_vim.nfvi._nfvi_infrastructure_module import nfvi_get_logs # noqa: F401 @@ -124,6 +125,7 @@ from nfv_vim.nfvi._nfvi_module import nfvi_finalize # noqa: F401 from nfv_vim.nfvi._nfvi_module import nfvi_initialize # noqa: F401 from nfv_vim.nfvi._nfvi_module import nfvi_reinitialize # noqa: F401 +from nfv_vim.nfvi._nfvi_network_module import nfvi_add_router_to_agent # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_create_network # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_create_network_host_services # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_create_subnet # noqa: F401 @@ -131,13 +133,18 @@ from nfv_vim.nfvi._nfvi_network_module import nfvi_delete_network # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_delete_network_host_services # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_delete_subnet # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_enable_network_host_services # noqa: F401 +from nfv_vim.nfvi._nfvi_network_module import nfvi_get_agent_routers # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_get_network # noqa: F401 +from nfv_vim.nfvi._nfvi_network_module import nfvi_get_network_agents # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_get_networks # noqa: F401 +from nfv_vim.nfvi._nfvi_network_module import nfvi_get_physical_network # noqa: F401 +from nfv_vim.nfvi._nfvi_network_module import nfvi_get_router_ports # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_get_subnet # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_get_subnets # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_network_plugin_disabled # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_notify_network_host_disabled # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_query_network_host_services # noqa: F401 +from nfv_vim.nfvi._nfvi_network_module import nfvi_remove_router_from_agent # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_update_network # noqa: F401 from nfv_vim.nfvi._nfvi_network_module import nfvi_update_subnet # noqa: F401 diff --git a/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_infrastructure_module.py b/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_infrastructure_module.py index 6dee9ac7..5844038f 100755 --- a/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_infrastructure_module.py +++ b/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_infrastructure_module.py @@ -12,6 +12,16 @@ DLOG = debug.debug_get_logger('nfv_vim.nfvi.nfvi_infrastructure_module') _infrastructure_plugin = None +def nfvi_get_datanetworks(host_uuid, callback): + """ + Get host data network information + """ + cmd_id = _infrastructure_plugin.invoke_plugin('get_datanetworks', + host_uuid, + callback=callback) + return cmd_id + + def nfvi_get_system_info(callback): """ Get information about the system diff --git a/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_network_module.py b/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_network_module.py index 3e2898df..a55a45cd 100755 --- a/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_network_module.py +++ b/nfv/nfv-vim/nfv_vim/nfvi/_nfvi_network_module.py @@ -141,6 +141,60 @@ def nfvi_enable_network_host_services(host_uuid, host_name, host_personality, return cmd_id +def nfvi_get_network_agents(callback): + """ + Get network agents of all hosts + """ + cmd_id = _network_plugin.invoke_plugin('get_network_agents', + callback=callback) + return cmd_id + + +def nfvi_get_agent_routers(agent_id, callback): + """ + Get routers hosted on a l3 agent + """ + cmd_id = _network_plugin.invoke_plugin('get_agent_routers', + agent_id, callback=callback) + return cmd_id + + +def nfvi_get_router_ports(router_id, callback): + """ + Get router port information + """ + cmd_id = _network_plugin.invoke_plugin('get_router_ports', + router_id, callback=callback) + return cmd_id + + +def nfvi_add_router_to_agent(agent_id, router_id, callback): + """ + Add a router to an L3 agent + """ + cmd_id = _network_plugin.invoke_plugin('add_router_to_agent', + agent_id, router_id, callback=callback) + return cmd_id + + +def nfvi_remove_router_from_agent(agent_id, router_id, callback): + """ + Remove a router from an L3 Agent + """ + cmd_id = _network_plugin.invoke_plugin('remove_router_from_agent', + agent_id, router_id, callback=callback) + return cmd_id + + +def nfvi_get_physical_network(network_id, callback): + """ + Get physical network of a network + """ + cmd_id = _network_plugin.invoke_plugin('get_physical_network', + network_id, callback=callback) + return cmd_id + + def nfvi_delete_network_host_services(host_uuid, host_name, host_personality, callback): """ diff --git a/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_infrastructure_api.py b/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_infrastructure_api.py index b5040c0b..4395318e 100755 --- a/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_infrastructure_api.py +++ b/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_infrastructure_api.py @@ -40,6 +40,13 @@ class NFVIInfrastructureAPI(object): """ pass + @abc.abstractmethod + def get_datanetworks(self, future, host_uuid, callback): + """ + Get data networks on a host from the plugin + """ + pass + @abc.abstractmethod def get_system_info(self, future, callback): """ diff --git a/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_network_api.py b/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_network_api.py index 0d345743..737ff45b 100755 --- a/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_network_api.py +++ b/nfv/nfv-vim/nfv_vim/nfvi/api/v1/_nfvi_network_api.py @@ -130,6 +130,48 @@ class NFVINetworkAPI(object): """ pass + @abc.abstractmethod + def get_network_agents(self, future, callback): + """ + Get network agent information using the plugin + """ + pass + + @abc.abstractmethod + def get_agent_routers(self, future, agent_id, callback): + """ + Get network routers on a hosting agent using the plugin + """ + pass + + @abc.abstractmethod + def get_router_ports(self, future, router_id, callback): + """ + Get router ports using the plugin + """ + pass + + @abc.abstractmethod + def add_router_to_agent(self, future, agent_id, router_id, callback): + """ + Add a router to an agent using the plugin + """ + pass + + @abc.abstractmethod + def remove_router_from_agent(self, future, agent_id, router_id, callback): + """ + Remove router from an agent using the plugin + """ + pass + + @abc.abstractmethod + def get_physical_network(self, future, network_id, callback): + """ + Get physical network of a network using the plugin + """ + pass + @abc.abstractmethod def delete_host_services(self, future, host_uuid, host_name, host_personality, callback): diff --git a/nfv/nfv-vim/nfv_vim/vim.py b/nfv/nfv-vim/nfv_vim/vim.py index 59756afe..3aaf5b4b 100755 --- a/nfv/nfv-vim/nfv_vim/vim.py +++ b/nfv/nfv-vim/nfv_vim/vim.py @@ -23,6 +23,7 @@ from nfv_vim import database from nfv_vim import directors from nfv_vim import dor from nfv_vim import events +from nfv_vim import network_rebalance from nfv_vim import nfvi from nfv_vim import tables @@ -85,6 +86,7 @@ def process_initialize(): events.events_initialize() audits.audits_initialize() dor.dor_initialize() + network_rebalance.nr_initialize() return init_complete @@ -107,6 +109,7 @@ def process_finalize(): Virtual Infrastructure Manager - Finalize """ dor.dor_finalize() + network_rebalance.nr_finalize() audits.audits_finalize() events.events_finalize() directors.directors_finalize()