You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2150 lines
88 KiB
2150 lines
88 KiB
# Copyright (c) 2013 OpenStack Foundation |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
# you may not use this file except in compliance with the License. |
|
# You may obtain a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|
# implied. |
|
# See the License for the specific language governing permissions and |
|
# limitations under the License. |
|
|
|
import functools |
|
import operator |
|
import socket |
|
|
|
import mock |
|
from mock import patch |
|
from neutron_lib.api.definitions import portbindings |
|
from neutron_lib import constants as n_const |
|
from neutron_lib.db import api as db_api |
|
from neutron_lib.plugins import constants as plugin_constants |
|
from neutron_lib.plugins import directory |
|
from neutron_lib.plugins.ml2 import api as driver_api |
|
from oslo_config import cfg |
|
from oslo_utils import importutils |
|
import six |
|
|
|
from neutron.tests import base |
|
from neutron.tests.unit import testlib_api |
|
|
|
from networking_arista.common import db_lib |
|
from networking_arista.common import exceptions as arista_exc |
|
from networking_arista.ml2 import arista_ml2 |
|
from networking_arista.ml2 import mechanism_arista |
|
import networking_arista.tests.unit.ml2.utils as utils |
|
|
|
|
|
def setup_valid_config(): |
|
utils.setup_arista_wrapper_config(cfg) |
|
|
|
|
|
class AristaProvisionedVlansStorageTestCase(testlib_api.SqlTestCase): |
|
"""Test storing and retriving functionality of Arista mechanism driver. |
|
|
|
Tests all methods of this class by invoking them separately as well |
|
as a group. |
|
""" |
|
|
|
def test_tenant_is_remembered(self): |
|
tenant_id = 'test' |
|
|
|
db_lib.remember_tenant(tenant_id) |
|
net_provisioned = db_lib.is_tenant_provisioned(tenant_id) |
|
self.assertTrue(net_provisioned, 'Tenant must be provisioned') |
|
|
|
def test_tenant_is_removed(self): |
|
tenant_id = 'test' |
|
|
|
db_lib.remember_tenant(tenant_id) |
|
db_lib.forget_tenant(tenant_id) |
|
net_provisioned = db_lib.is_tenant_provisioned(tenant_id) |
|
self.assertFalse(net_provisioned, 'The Tenant should be deleted') |
|
|
|
def test_network_is_remembered(self): |
|
tenant_id = 'test' |
|
network_id = '123' |
|
segmentation_id = 456 |
|
segment_id = 'segment_id_%s' % segmentation_id |
|
|
|
db_lib.remember_network_segment(tenant_id, network_id, segmentation_id, |
|
segment_id) |
|
net_provisioned = db_lib.is_network_provisioned(tenant_id, |
|
network_id) |
|
self.assertTrue(net_provisioned, 'Network must be provisioned') |
|
|
|
def test_network_is_removed(self): |
|
tenant_id = 'test' |
|
network_id = '123' |
|
segment_id = 'segment_id_1' |
|
|
|
db_lib.remember_network_segment(tenant_id, network_id, '123', |
|
segment_id) |
|
db_lib.forget_network_segment(tenant_id, network_id) |
|
net_provisioned = db_lib.is_network_provisioned(tenant_id, network_id) |
|
self.assertFalse(net_provisioned, 'The network should be deleted') |
|
|
|
def test_vm_is_remembered(self): |
|
vm_id = 'VM-1' |
|
tenant_id = 'test' |
|
network_id = '123' |
|
port_id = 456 |
|
host_id = 'ubuntu1' |
|
|
|
db_lib.remember_vm(vm_id, host_id, port_id, network_id, tenant_id) |
|
vm_provisioned = db_lib.is_vm_provisioned(vm_id, host_id, port_id, |
|
network_id, tenant_id) |
|
self.assertTrue(vm_provisioned, 'VM must be provisioned') |
|
|
|
def test_vm_is_removed(self): |
|
vm_id = 'VM-1' |
|
tenant_id = 'test' |
|
network_id = '123' |
|
port_id = 456 |
|
host_id = 'ubuntu1' |
|
|
|
db_lib.remember_vm(vm_id, host_id, port_id, network_id, tenant_id) |
|
db_lib.forget_port(port_id, host_id) |
|
vm_provisioned = db_lib.is_vm_provisioned(vm_id, host_id, port_id, |
|
network_id, tenant_id) |
|
self.assertFalse(vm_provisioned, 'The vm should be deleted') |
|
|
|
def test_remembers_multiple_networks(self): |
|
tenant_id = 'test' |
|
expected_num_nets = 100 |
|
segment_id = 'segment_%s' |
|
nets = ['id%s' % n for n in range(expected_num_nets)] |
|
for net_id in nets: |
|
db_lib.remember_network_segment(tenant_id, net_id, 123, |
|
segment_id % net_id) |
|
|
|
num_nets_provisioned = db_lib.num_nets_provisioned(tenant_id) |
|
self.assertEqual(expected_num_nets, num_nets_provisioned, |
|
'There should be %d nets, not %d' % |
|
(expected_num_nets, num_nets_provisioned)) |
|
|
|
def test_removes_all_networks(self): |
|
tenant_id = 'test' |
|
num_nets = 100 |
|
old_nets = db_lib.num_nets_provisioned(tenant_id) |
|
nets = ['id_%s' % n for n in range(num_nets)] |
|
segment_id = 'segment_%s' |
|
for net_id in nets: |
|
db_lib.remember_network_segment(tenant_id, net_id, 123, |
|
segment_id % net_id) |
|
for net_id in nets: |
|
db_lib.forget_network_segment(tenant_id, net_id) |
|
|
|
num_nets_provisioned = db_lib.num_nets_provisioned(tenant_id) |
|
expected = old_nets |
|
self.assertEqual(expected, num_nets_provisioned, |
|
'There should be %d nets, not %d' % |
|
(expected, num_nets_provisioned)) |
|
|
|
def test_remembers_multiple_tenants(self): |
|
expected_num_tenants = 100 |
|
tenants = ['id%s' % n for n in range(expected_num_tenants)] |
|
for tenant_id in tenants: |
|
db_lib.remember_tenant(tenant_id) |
|
|
|
num_tenants_provisioned = db_lib.num_provisioned_tenants() |
|
self.assertEqual(expected_num_tenants, num_tenants_provisioned, |
|
'There should be %d tenants, not %d' % |
|
(expected_num_tenants, num_tenants_provisioned)) |
|
|
|
def test_removes_multiple_tenants(self): |
|
num_tenants = 100 |
|
tenants = ['id%s' % n for n in range(num_tenants)] |
|
for tenant_id in tenants: |
|
db_lib.remember_tenant(tenant_id) |
|
for tenant_id in tenants: |
|
db_lib.forget_tenant(tenant_id) |
|
|
|
num_tenants_provisioned = db_lib.num_provisioned_tenants() |
|
expected = 0 |
|
self.assertEqual(expected, num_tenants_provisioned, |
|
'There should be %d tenants, not %d' % |
|
(expected, num_tenants_provisioned)) |
|
|
|
def test_num_vm_is_valid(self): |
|
tenant_id = 'test' |
|
network_id = '123' |
|
port_id_base = 'port-id' |
|
host_id = 'ubuntu1' |
|
|
|
vm_to_remember = ['vm1', 'vm2', 'vm3'] |
|
vm_to_forget = ['vm2', 'vm1'] |
|
|
|
for vm in vm_to_remember: |
|
port_id = port_id_base + vm |
|
db_lib.remember_vm(vm, host_id, port_id, network_id, tenant_id) |
|
for vm in vm_to_forget: |
|
port_id = port_id_base + vm |
|
db_lib.forget_port(port_id, host_id) |
|
|
|
num_vms = len(db_lib.get_vms(tenant_id)) |
|
expected = len(vm_to_remember) - len(vm_to_forget) |
|
|
|
self.assertEqual(expected, num_vms, |
|
'There should be %d records, ' |
|
'got %d records' % (expected, num_vms)) |
|
# clean up afterwards |
|
db_lib.forget_port(port_id, host_id) |
|
|
|
def test_get_network_list_returns_eos_compatible_data(self): |
|
tenant = u'test-1' |
|
segm_type = 'vlan' |
|
network_id = u'123' |
|
network2_id = u'1234' |
|
vlan_id = 123 |
|
vlan2_id = 1234 |
|
segment_id1 = '11111-%s' % vlan_id |
|
segment_id2 = '11111-%s' % vlan2_id |
|
expected_eos_net_list = {network_id: {u'networkId': network_id, |
|
u'segmentationTypeId': vlan_id, |
|
u'tenantId': tenant, |
|
u'segmentId': segment_id1, |
|
u'segmentationType': segm_type}, |
|
network2_id: {u'networkId': network2_id, |
|
u'tenantId': tenant, |
|
u'segmentId': segment_id2, |
|
u'segmentationTypeId': vlan2_id, |
|
u'segmentationType': segm_type}} |
|
|
|
db_lib.remember_network_segment(tenant, |
|
network_id, vlan_id, segment_id1) |
|
db_lib.remember_network_segment(tenant, |
|
network2_id, vlan2_id, segment_id2) |
|
|
|
net_list = db_lib.get_networks(tenant) |
|
self.assertEqual(net_list, expected_eos_net_list, ('%s != %s' % |
|
(net_list, expected_eos_net_list))) |
|
|
|
|
|
BASE_RPC = "networking_arista.ml2.arista_ml2.AristaRPCWrapperJSON." |
|
JSON_SEND_FUNC = BASE_RPC + "_send_api_request" |
|
RAND_FUNC = BASE_RPC + "_get_random_name" |
|
EAPI_SEND_FUNC = ('networking_arista.ml2.arista_ml2.AristaRPCWrapperEapi' |
|
'._send_eapi_req') |
|
|
|
|
|
def port_dict_representation(port): |
|
return {port['portId']: {'device_owner': port['device_owner'], |
|
'device_id': port['device_id'], |
|
'name': port['name'], |
|
'id': port['portId'], |
|
'tenant_id': port['tenant_id'], |
|
'network_id': port['network_id']}} |
|
|
|
|
|
class _UnorderedDictList(list): |
|
def __init__(self, iterable='', sort_key=None): |
|
super(_UnorderedDictList, self).__init__(iterable) |
|
try: |
|
(self[0] or {})[sort_key] |
|
self.sort_key = sort_key |
|
except (IndexError, KeyError): |
|
self.sort_key = None |
|
|
|
def __eq__(self, other): |
|
if isinstance(other, list) and self.sort_key: |
|
key = operator.itemgetter(self.sort_key) |
|
return sorted(self, key=key) == sorted(other, key=key) |
|
else: |
|
return super(_UnorderedDictList, self).__eq__(other) |
|
|
|
|
|
class TestAristaJSONRPCWrapper(testlib_api.SqlTestCase): |
|
def setUp(self): |
|
super(TestAristaJSONRPCWrapper, self).setUp() |
|
plugin_klass = importutils.import_class( |
|
"neutron.db.db_base_plugin_v2.NeutronDbPluginV2") |
|
directory.add_plugin(plugin_constants.CORE, plugin_klass()) |
|
setup_valid_config() |
|
ndb = db_lib.NeutronNets() |
|
self.drv = arista_ml2.AristaRPCWrapperJSON(ndb) |
|
self.drv._server_ip = "10.11.12.13" |
|
self.region = 'RegionOne' |
|
|
|
def _verify_send_api_request_call(self, mock_send_api_req, calls, |
|
unordered_dict_list=False): |
|
if unordered_dict_list: |
|
wrapper = functools.partial(_UnorderedDictList, sort_key='id') |
|
else: |
|
wrapper = lambda x: x |
|
|
|
expected_calls = [ |
|
mock.call(c[0], c[1], *(wrapper(d) for d in c[2:])) for c in calls |
|
] |
|
|
|
mock_send_api_req.assert_has_calls(expected_calls, any_order=True) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_register_with_eos(self, mock_send_api_req): |
|
self.drv.register_with_eos() |
|
calls = [ |
|
('region/RegionOne', 'PUT', |
|
[{'name': 'RegionOne', 'syncInterval': 10}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
def _get_random_name(self): |
|
return 'thisWillBeRandomInProd' |
|
|
|
@patch(JSON_SEND_FUNC) |
|
@patch(RAND_FUNC, _get_random_name) |
|
def test_sync_start(self, mock_send_api_req): |
|
mock_send_api_req.side_effect = [ |
|
[{'name': 'RegionOne', 'syncStatus': ''}], |
|
[{}], |
|
[{'syncStatus': 'syncInProgress', |
|
'requestId': self._get_random_name()}] |
|
] |
|
assert self.drv.sync_start() |
|
calls = [ |
|
('region/RegionOne/sync', 'POST', |
|
{'requester': socket.gethostname().split('.')[0], |
|
'requestId': self._get_random_name()}) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
@patch(RAND_FUNC, _get_random_name) |
|
def test_sync_end(self, mock_send_api_req): |
|
mock_send_api_req.return_value = [{'requester': |
|
self._get_random_name()}] |
|
self.drv.current_sync_name = self._get_random_name() |
|
assert self.drv.sync_end() |
|
calls = [ |
|
('region/RegionOne/sync', 'DELETE') |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_create_region(self, mock_send_api_req): |
|
self.drv.create_region('foo') |
|
calls = [('region/', 'POST', [{'name': 'foo'}])] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_delete_region(self, mock_send_api_req): |
|
self.drv.delete_region('foo') |
|
calls = [('region/', 'DELETE', [{'name': 'foo'}])] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_get_tenants(self, mock_send_api_req): |
|
self.drv.get_tenants() |
|
calls = [('region/RegionOne/tenant', 'GET')] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_delete_tenant_bulk(self, mock_send_api_req): |
|
self.drv.delete_tenant_bulk(['t1', 't2']) |
|
calls = [('region/RegionOne/tenant', 'DELETE', |
|
[{'id': 't1'}, {'id': 't2'}])] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
def _createNetworkData(self, tenant_id, network_id, shared=False, |
|
seg_id=100, network_type='vlan'): |
|
return { |
|
'network_id': network_id, |
|
'tenantId': tenant_id, |
|
'shared': shared, |
|
'segments': [{'segmentation_id': seg_id, |
|
'physical_network': 'default', |
|
'id': 'segment_id_1', |
|
'is_dynamic': False, |
|
'network_type': network_type}], |
|
} |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_create_network_bulk(self, mock_send_api_req): |
|
n = [] |
|
n.append(self._createNetworkData('t1', 'net1', seg_id=100)) |
|
n.append(self._createNetworkData('t1', 'net2', seg_id=200)) |
|
n.append(self._createNetworkData('t1', 'net3', network_type='flat')) |
|
self.drv.create_network_bulk('t1', n) |
|
calls = [ |
|
('region/RegionOne/network', 'POST', |
|
[{'id': 'net1', 'tenantId': 't1', 'shared': False}, |
|
{'id': 'net2', 'tenantId': 't1', 'shared': False}, |
|
{'id': 'net3', 'tenantId': 't1', 'shared': False}]), |
|
('region/RegionOne/segment', 'POST', |
|
[{'id': 'segment_id_1', 'networkId': 'net1', 'type': 'vlan', |
|
'segmentationId': 100, 'segmentType': 'static'}, |
|
{'id': 'segment_id_1', 'networkId': 'net2', 'type': 'vlan', |
|
'segmentationId': 200, 'segmentType': 'static'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls, True) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_delete_network_bulk(self, mock_send_api_req): |
|
self.drv.delete_network_bulk('t1', ['net1', 'net2']) |
|
calls = [ |
|
('region/RegionOne/network', 'DELETE', |
|
[{'id': 'net1', 'tenantId': 't1'}, |
|
{'id': 'net2', 'tenantId': 't1'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls, True) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_create_network_segments(self, mock_send_api_req): |
|
segments = [{'segmentation_id': 101, |
|
'physical_network': 'default', |
|
'id': 'segment_id_1', |
|
'is_dynamic': False, |
|
'network_type': 'vlan'}, |
|
{'segmentation_id': 102, |
|
'physical_network': 'default', |
|
'id': 'segment_id_2', |
|
'is_dynamic': True, |
|
'network_type': 'vlan'}] |
|
self.drv.create_network_segments('t1', 'n1', 'net1', segments) |
|
calls = [ |
|
('region/RegionOne/segment', 'POST', |
|
[{'id': 'segment_id_1', 'networkId': 'n1', 'type': 'vlan', |
|
'segmentationId': 101, 'segmentType': 'static'}, |
|
{'id': 'segment_id_2', 'networkId': 'n1', 'type': 'vlan', |
|
'segmentationId': 102, 'segmentType': 'dynamic'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls, True) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_delete_network_segments(self, mock_send_api_req): |
|
segments = [{'segmentation_id': 101, |
|
'physical_network': 'default', |
|
'id': 'segment_id_1', |
|
'is_dynamic': False, |
|
'network_type': 'vlan'}, |
|
{'segmentation_id': 102, |
|
'physical_network': 'default', |
|
'id': 'segment_id_2', |
|
'is_dynamic': True, |
|
'network_type': 'vlan'}] |
|
self.drv.delete_network_segments('t1', segments) |
|
calls = [ |
|
('region/RegionOne/segment', 'DELETE', |
|
[{'id': 'segment_id_1'}, |
|
{'id': 'segment_id_2'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_create_instance_bulk(self, mock_send_api_req): |
|
tenant_id = 'ten-3' |
|
num_devices = 8 |
|
num_ports_per_device = 2 |
|
|
|
device_count = 0 |
|
devices = {} |
|
for device_id in range(0, num_devices): |
|
dev_id = 'dev-id-%d' % device_id |
|
devices[dev_id] = {'vmId': dev_id, |
|
'baremetal_instance': False, |
|
'ports': [] |
|
} |
|
for port_id in range(0, num_ports_per_device): |
|
port_id = 'port-id-%d-%d' % (device_id, port_id) |
|
port = { |
|
'device_id': 'dev-id-%d' % device_id, |
|
'hosts': ['host_%d' % (device_count)], |
|
'portId': port_id |
|
} |
|
devices[dev_id]['ports'].append(port) |
|
device_count += 1 |
|
|
|
device_owners = [n_const.DEVICE_OWNER_DHCP, |
|
'compute', |
|
'baremetal', |
|
n_const.DEVICE_OWNER_DVR_INTERFACE] |
|
port_list = [] |
|
|
|
net_count = 0 |
|
for device_id in range(0, num_devices): |
|
for port_id in range(0, num_ports_per_device): |
|
port = { |
|
'portId': 'port-id-%d-%d' % (device_id, port_id), |
|
'device_id': 'dev-id-%d' % device_id, |
|
'device_owner': device_owners[device_id % 4], |
|
'network_id': 'network-id-%d' % net_count, |
|
'name': 'port-%d-%d' % (device_id, port_id), |
|
'tenant_id': tenant_id, |
|
} |
|
port_list.append(port) |
|
net_count += 1 |
|
|
|
create_ports = {} |
|
for port in port_list: |
|
create_ports.update(port_dict_representation(port)) |
|
|
|
profiles = {} |
|
for port in port_list: |
|
profiles[port['portId']] = {'vnic_type': 'normal'} |
|
if port['device_owner'] == 'baremetal': |
|
profiles[port['portId']] = { |
|
'vnic_type': 'baremetal', |
|
'profile': '{"local_link_information":' |
|
'[{"switch_id": "switch01", "port_id": "Ethernet1"}]}'} |
|
self.drv.create_instance_bulk(tenant_id, create_ports, devices, |
|
profiles) |
|
calls = [ |
|
('region/RegionOne/tenant?tenantId=ten-3', 'GET'), |
|
('region/RegionOne/dhcp?tenantId=ten-3', 'POST', |
|
[{'id': 'dev-id-0', 'hostId': 'host_0'}, |
|
{'id': 'dev-id-4', 'hostId': 'host_4'}]), |
|
('region/RegionOne/vm?tenantId=ten-3', 'POST', |
|
[{'id': 'dev-id-1', 'hostId': 'host_1'}, |
|
{'id': 'dev-id-5', 'hostId': 'host_5'}]), |
|
('region/RegionOne/baremetal?tenantId=ten-3', 'POST', |
|
[{'id': 'dev-id-2', 'hostId': 'host_2'}, |
|
{'id': 'dev-id-6', 'hostId': 'host_6'}]), |
|
('region/RegionOne/router?tenantId=ten-3', 'POST', |
|
[{'id': 'dev-id-3', 'hostId': 'host_3'}, |
|
{'id': 'dev-id-7', 'hostId': 'host_7'}]), |
|
('region/RegionOne/port', 'POST', |
|
[{'networkId': 'network-id-0', 'id': 'port-id-0-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-0', |
|
'name': 'port-0-0', 'hosts': ['host_0'], |
|
'instanceType': 'dhcp', 'vlanType': 'allowed'}, |
|
{'networkId': 'network-id-1', 'id': 'port-id-0-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-0', |
|
'name': 'port-0-1', 'hosts': ['host_0'], |
|
'instanceType': 'dhcp', 'vlanType': 'allowed'}, |
|
|
|
{'networkId': 'network-id-2', 'id': 'port-id-1-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-1', |
|
'name': 'port-1-0', 'hosts': ['host_1'], |
|
'instanceType': 'vm', 'vlanType': 'allowed'}, |
|
{'networkId': 'network-id-3', 'id': 'port-id-1-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-1', |
|
'name': 'port-1-1', 'hosts': ['host_1'], |
|
'instanceType': 'vm', 'vlanType': 'allowed'}, |
|
|
|
{'networkId': 'network-id-4', 'id': 'port-id-2-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-2', |
|
'name': 'port-2-0', 'hosts': ['host_2'], |
|
'instanceType': 'baremetal', 'vlanType': 'native'}, |
|
{'networkId': 'network-id-5', 'id': 'port-id-2-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-2', |
|
'name': 'port-2-1', 'hosts': ['host_2'], |
|
'instanceType': 'baremetal', 'vlanType': 'native'}, |
|
|
|
{'networkId': 'network-id-6', 'id': 'port-id-3-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-3', |
|
'name': 'port-3-0', 'hosts': ['host_3'], |
|
'instanceType': 'router', 'vlanType': 'allowed'}, |
|
{'networkId': 'network-id-7', 'id': 'port-id-3-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-3', |
|
'name': 'port-3-1', 'hosts': ['host_3'], |
|
'instanceType': 'router', 'vlanType': 'allowed'}, |
|
|
|
{'networkId': 'network-id-8', 'id': 'port-id-4-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-4', |
|
'name': 'port-4-0', 'hosts': ['host_4'], |
|
'instanceType': 'dhcp', 'vlanType': 'allowed'}, |
|
{'networkId': 'network-id-9', 'id': 'port-id-4-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-4', |
|
'name': 'port-4-1', 'hosts': ['host_4'], |
|
'instanceType': 'dhcp', 'vlanType': 'allowed'}, |
|
|
|
{'networkId': 'network-id-10', 'id': 'port-id-5-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-5', |
|
'name': 'port-5-0', 'hosts': ['host_5'], |
|
'instanceType': 'vm', 'vlanType': 'allowed'}, |
|
{'networkId': 'network-id-11', 'id': 'port-id-5-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-5', |
|
'name': 'port-5-1', 'hosts': ['host_5'], |
|
'instanceType': 'vm', 'vlanType': 'allowed'}, |
|
|
|
{'networkId': 'network-id-12', 'id': 'port-id-6-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-6', |
|
'name': 'port-6-0', 'hosts': ['host_6'], |
|
'instanceType': 'baremetal', 'vlanType': 'native'}, |
|
{'networkId': 'network-id-13', 'id': 'port-id-6-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-6', |
|
'name': 'port-6-1', 'hosts': ['host_6'], |
|
'instanceType': 'baremetal', 'vlanType': 'native'}, |
|
|
|
{'networkId': 'network-id-14', 'id': 'port-id-7-0', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-7', |
|
'name': 'port-7-0', 'hosts': ['host_7'], |
|
'instanceType': 'router', 'vlanType': 'allowed'}, |
|
{'networkId': 'network-id-15', 'id': 'port-id-7-1', |
|
'tenantId': 'ten-3', 'instanceId': 'dev-id-7', |
|
'name': 'port-7-1', 'hosts': ['host_7'], |
|
'instanceType': 'router', 'vlanType': 'allowed'}]), |
|
|
|
('region/RegionOne/port/port-id-0-0/binding', |
|
'POST', [{'portId': 'port-id-0-0', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_0'}]}]), |
|
('region/RegionOne/port/port-id-0-1/binding', |
|
'POST', [{'portId': 'port-id-0-1', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_0'}]}]), |
|
|
|
('region/RegionOne/port/port-id-1-0/binding', |
|
'POST', [{'portId': 'port-id-1-0', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_1'}]}]), |
|
('region/RegionOne/port/port-id-1-1/binding', |
|
'POST', [{'portId': 'port-id-1-1', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_1'}]}]), |
|
|
|
('region/RegionOne/port/port-id-2-0/binding', |
|
'POST', [{'portId': 'port-id-2-0', 'switchBinding': [ |
|
{'interface': u'Ethernet1', 'host': 'host_2', |
|
'segment': [], 'switch': u'switch01'}]}]), |
|
('region/RegionOne/port/port-id-2-1/binding', |
|
'POST', [{'portId': 'port-id-2-1', 'switchBinding': [ |
|
{'interface': u'Ethernet1', 'host': 'host_2', |
|
'segment': [], 'switch': u'switch01'}]}]), |
|
|
|
('region/RegionOne/port/port-id-3-0/binding', |
|
'POST', [{'portId': 'port-id-3-0', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_3'}]}]), |
|
('region/RegionOne/port/port-id-3-1/binding', |
|
'POST', [{'portId': 'port-id-3-1', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_3'}]}]), |
|
|
|
('region/RegionOne/port/port-id-4-0/binding', |
|
'POST', [{'portId': 'port-id-4-0', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_4'}]}]), |
|
('region/RegionOne/port/port-id-4-1/binding', |
|
'POST', [{'portId': 'port-id-4-1', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_4'}]}]), |
|
|
|
('region/RegionOne/port/port-id-5-0/binding', |
|
'POST', [{'portId': 'port-id-5-0', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_5'}]}]), |
|
('region/RegionOne/port/port-id-5-1/binding', |
|
'POST', [{'portId': 'port-id-5-1', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_5'}]}]), |
|
|
|
('region/RegionOne/port/port-id-6-0/binding', |
|
'POST', [{'portId': 'port-id-6-0', 'switchBinding': [ |
|
{'interface': u'Ethernet1', 'host': 'host_6', |
|
'segment': [], 'switch': u'switch01'}]}]), |
|
('region/RegionOne/port/port-id-6-1/binding', |
|
'POST', [{'portId': 'port-id-6-1', 'switchBinding': [ |
|
{'interface': u'Ethernet1', 'host': 'host_6', |
|
'segment': [], 'switch': u'switch01'}]}]), |
|
|
|
('region/RegionOne/port/port-id-7-0/binding', |
|
'POST', [{'portId': 'port-id-7-0', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_7'}]}]), |
|
('region/RegionOne/port/port-id-7-1/binding', |
|
'POST', [{'portId': 'port-id-7-1', 'hostBinding': [ |
|
{'segment': [], 'host': 'host_7'}]}]), |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls, True) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_delete_vm_bulk(self, mock_send_api_req): |
|
self.drv.delete_vm_bulk('t1', ['vm1', 'vm2']) |
|
calls = [ |
|
('region/RegionOne/vm', 'DELETE', |
|
[{'id': 'vm1'}, {'id': 'vm2'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_delete_dhcp_bulk(self, mock_send_api_req): |
|
self.drv.delete_dhcp_bulk('t1', ['dhcp1', 'dhcp2']) |
|
calls = [ |
|
('region/RegionOne/dhcp', 'DELETE', |
|
[{'id': 'dhcp1'}, {'id': 'dhcp2'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_delete_port(self, mock_send_api_req): |
|
self.drv.delete_port('p1', 'inst1', 'vm') |
|
self.drv.delete_port('p2', 'inst2', 'dhcp') |
|
calls = [ |
|
('region/RegionOne/port?portId=p1&id=inst1&type=vm', |
|
'DELETE', |
|
[{'hosts': [], 'id': 'p1', 'tenantId': None, 'networkId': None, |
|
'instanceId': 'inst1', 'name': None, 'instanceType': 'vm', |
|
'vlanType': 'allowed'}]), |
|
('region/RegionOne/port?portId=p2&id=inst2&type=dhcp', |
|
'DELETE', |
|
[{'hosts': [], 'id': 'p2', 'tenantId': None, 'networkId': None, |
|
'instanceId': 'inst2', 'name': None, 'instanceType': 'dhcp', |
|
'vlanType': 'allowed'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_get_port(self, mock_send_api_req): |
|
self.drv.get_instance_ports('inst1', 'vm') |
|
calls = [ |
|
('region/RegionOne/port?id=inst1&type=vm', |
|
'GET') |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_plug_virtual_port_into_network(self, mock_send_api_req): |
|
segments = [{'segmentation_id': 101, |
|
'id': 'segment_id_1', |
|
'network_type': 'vlan', |
|
'is_dynamic': False}] |
|
self.drv.plug_port_into_network('vm1', 'h1', 'p1', 'n1', 't1', 'port1', |
|
'compute', None, None, None, segments) |
|
calls = [ |
|
('region/RegionOne/vm?tenantId=t1', 'POST', |
|
[{'id': 'vm1', 'hostId': 'h1'}]), |
|
('region/RegionOne/port', 'POST', |
|
[{'id': 'p1', 'hosts': ['h1'], 'tenantId': 't1', |
|
'networkId': 'n1', 'instanceId': 'vm1', 'name': 'port1', |
|
'instanceType': 'vm', 'vlanType': 'allowed'}]), |
|
('region/RegionOne/port/p1/binding', 'POST', |
|
[{'portId': 'p1', 'hostBinding': [{'host': 'h1', 'segment': [{ |
|
'id': 'segment_id_1', 'type': 'vlan', 'segmentationId': 101, |
|
'networkId': 'n1', 'segment_type': 'static'}]}]}]), |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
@patch('networking_arista.ml2.arista_ml2.AristaRPCWrapperJSON.' |
|
'get_instance_ports') |
|
def test_unplug_virtual_port_from_network(self, mock_get_instance_ports, |
|
mock_send_api_req): |
|
mock_get_instance_ports.return_value = [] |
|
self.drv.unplug_port_from_network('vm1', 'compute', 'h1', 'p1', 'n1', |
|
't1', None, None) |
|
port = self.drv._create_port_data('p1', None, None, 'vm1', None, 'vm', |
|
None) |
|
calls = [ |
|
('region/RegionOne/port/p1/binding', 'DELETE', |
|
[{'portId': 'p1', 'hostBinding': [{'host': 'h1'}]}]), |
|
('region/RegionOne/port?portId=p1&id=vm1&type=vm', |
|
'DELETE', [port]), |
|
('region/RegionOne/vm', 'DELETE', [{'id': 'vm1'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_plug_baremetal_port_into_network(self, mock_send_api_req): |
|
segments = [{'segmentation_id': 101, |
|
'id': 'segment_id_1', |
|
'network_type': 'vlan', |
|
'is_dynamic': False}] |
|
sg = {'id': 'security-group-1'} |
|
switch_bindings = [{'switch_id': 'switch01', 'port_id': 'Ethernet1', |
|
'switch_info': 'switch01'}] |
|
self.drv.plug_port_into_network('bm1', 'h1', 'p1', 'n1', 't1', 'port1', |
|
'baremetal', sg, None, 'baremetal', |
|
segments, |
|
switch_bindings=switch_bindings) |
|
calls = [ |
|
('region/RegionOne/baremetal?tenantId=t1', 'POST', |
|
[{'id': 'bm1', 'hostId': 'h1'}]), |
|
('region/RegionOne/port', 'POST', |
|
[{'id': 'p1', 'hosts': ['h1'], 'tenantId': 't1', |
|
'networkId': 'n1', 'instanceId': 'bm1', 'name': 'port1', |
|
'instanceType': 'baremetal', 'vlanType': 'native'}]), |
|
('region/RegionOne/port/p1/binding', 'POST', |
|
[{'portId': 'p1', 'switchBinding': [{'host': 'h1', |
|
'switch': 'switch01', 'interface': 'Ethernet1', 'segment': [{ |
|
'id': 'segment_id_1', 'type': 'vlan', 'segmentationId': 101, |
|
'networkId': 'n1', 'segment_type': 'static'}]}]}]), |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
@patch('networking_arista.ml2.arista_ml2.AristaRPCWrapperJSON.' |
|
'get_instance_ports') |
|
def test_unplug_baremetal_port_from_network(self, mock_get_instance_ports, |
|
mock_send_api_req): |
|
mock_get_instance_ports.return_value = [] |
|
switch_bindings = [{'switch_id': 'switch01', 'port_id': 'Ethernet1'}] |
|
self.drv.unplug_port_from_network('bm1', 'baremetal', 'h1', 'p1', 'n1', |
|
't1', None, 'baremetal', |
|
switch_bindings) |
|
port = self.drv._create_port_data('p1', None, None, 'bm1', None, |
|
'baremetal', None) |
|
calls = [ |
|
('region/RegionOne/port/p1/binding', 'DELETE', |
|
[{'portId': 'p1', 'switchBinding': |
|
[{'host': 'h1', 'switch': 'switch01', 'segment': [], |
|
'interface': 'Ethernet1'}]}]), |
|
('region/RegionOne/port?portId=p1&id=bm1&type=baremetal', |
|
'DELETE', [port]), |
|
('region/RegionOne/baremetal', 'DELETE', [{'id': 'bm1'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_plug_dhcp_port_into_network(self, mock_send_api_req): |
|
segments = [{'segmentation_id': 101, |
|
'id': 'segment_id_1', |
|
'network_type': 'vlan', |
|
'is_dynamic': False}] |
|
self.drv.plug_port_into_network('vm1', 'h1', 'p1', 'n1', 't1', 'port1', |
|
n_const.DEVICE_OWNER_DHCP, None, None, |
|
None, segments) |
|
calls = [ |
|
('region/RegionOne/dhcp?tenantId=t1', 'POST', |
|
[{'id': 'vm1', 'hostId': 'h1'}]), |
|
('region/RegionOne/port', 'POST', |
|
[{'id': 'p1', 'hosts': ['h1'], 'tenantId': 't1', |
|
'networkId': 'n1', 'instanceId': 'vm1', 'name': 'port1', |
|
'instanceType': 'dhcp', 'vlanType': 'allowed'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
@patch('networking_arista.ml2.arista_ml2.AristaRPCWrapperJSON.' |
|
'get_instance_ports') |
|
def test_unplug_dhcp_port_from_network(self, mock_get_instance_ports, |
|
mock_send_api_req): |
|
mock_get_instance_ports.return_value = [] |
|
self.drv.unplug_port_from_network('dhcp1', n_const.DEVICE_OWNER_DHCP, |
|
'h1', 'p1', 'n1', 't1', None, None) |
|
calls = [ |
|
('region/RegionOne/port?portId=p1&id=dhcp1&type=dhcp', |
|
'DELETE', |
|
[{'id': 'p1', 'hosts': [], 'tenantId': None, 'networkId': None, |
|
'instanceId': 'dhcp1', 'name': None, 'instanceType': 'dhcp', |
|
'vlanType': 'allowed'}]), |
|
('region/RegionOne/dhcp', 'DELETE', |
|
[{'id': 'dhcp1'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
def test_plug_router_port_into_network(self, mock_send_api_req): |
|
segments = [{'segmentation_id': 101, |
|
'id': 'segment_id_1', |
|
'network_type': 'vlan', |
|
'is_dynamic': False}] |
|
self.drv.plug_port_into_network('router1', 'h1', 'p1', 'n1', 't1', |
|
'port1', |
|
n_const.DEVICE_OWNER_DVR_INTERFACE, |
|
None, None, None, segments) |
|
calls = [ |
|
('region/RegionOne/router?tenantId=t1', 'POST', |
|
[{'id': 'router1', 'hostId': 'h1'}]), |
|
('region/RegionOne/port', 'POST', |
|
[{'id': 'p1', 'hosts': ['h1'], 'tenantId': 't1', |
|
'networkId': 'n1', 'instanceId': 'router1', 'name': 'port1', |
|
'instanceType': 'router', 'vlanType': 'allowed'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
@patch(JSON_SEND_FUNC) |
|
@patch('networking_arista.ml2.arista_ml2.AristaRPCWrapperJSON.' |
|
'get_instance_ports') |
|
def test_unplug_router_port_from_network(self, mock_get_instance_ports, |
|
mock_send_api_req): |
|
mock_get_instance_ports.return_value = [] |
|
self.drv.unplug_port_from_network('router1', |
|
n_const.DEVICE_OWNER_DVR_INTERFACE, |
|
'h1', 'p1', 'n1', 't1', None, None) |
|
calls = [ |
|
('region/RegionOne/port?portId=p1&id=router1&type=router', |
|
'DELETE', |
|
[{'id': 'p1', 'hosts': [], 'tenantId': None, 'networkId': None, |
|
'instanceId': 'router1', 'name': None, 'instanceType': 'router', |
|
'vlanType': 'allowed'}]), |
|
('region/RegionOne/router', 'DELETE', |
|
[{'id': 'router1'}]) |
|
] |
|
self._verify_send_api_request_call(mock_send_api_req, calls) |
|
|
|
|
|
class PositiveRPCWrapperValidConfigTestCase(testlib_api.SqlTestCase): |
|
"""Test cases to test the RPC between Arista Driver and EOS. |
|
|
|
Tests all methods used to send commands between Arista Driver and EOS |
|
""" |
|
|
|
def setUp(self): |
|
super(PositiveRPCWrapperValidConfigTestCase, self).setUp() |
|
setup_valid_config() |
|
ndb = db_lib.NeutronNets() |
|
self.drv = arista_ml2.AristaRPCWrapperEapi(ndb) |
|
self.drv._server_ip = "10.11.12.13" |
|
self.region = 'RegionOne' |
|
|
|
def _get_exit_mode_cmds(self, modes): |
|
return ['exit'] * len(modes) |
|
|
|
def _verify_send_eapi_request_calls(self, mock_send_eapi_req, cmds, |
|
commands_to_log=None): |
|
calls = [] |
|
calls.extend( |
|
mock.call(cmds=cmd, commands_to_log=log_cmd) |
|
for cmd, log_cmd in six.moves.zip(cmds, commands_to_log or cmds)) |
|
mock_send_eapi_req.assert_has_calls(calls) |
|
|
|
def test_no_exception_on_correct_configuration(self): |
|
self.assertIsNotNone(self.drv) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_plug_host_into_network(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
vm_id = 'vm-1' |
|
port_id = 123 |
|
network_id = 'net-id' |
|
host = 'host' |
|
port_name = '123-port' |
|
segment_id = 'segment_id_1' |
|
segments = [{'network_type': 'vlan', 'physical_network': 'default', |
|
'segmentation_id': 1234, 'id': segment_id}] |
|
|
|
self.drv.plug_host_into_network(vm_id, host, port_id, |
|
network_id, tenant_id, segments, |
|
port_name) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', 'vm id vm-1 hostid host', |
|
'port id 123 name "123-port" network-id net-id', |
|
] |
|
for level, segment in enumerate(segments): |
|
cmd2.append('segment level %s id %s' % (level, segment['id'])) |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_plug_dhcp_port_into_network(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
vm_id = 'vm-1' |
|
port_id = 123 |
|
network_id = 'net-id' |
|
host = 'host' |
|
port_name = '123-port' |
|
segments = [] |
|
|
|
self.drv.plug_port_into_network(vm_id, host, port_id, network_id, |
|
tenant_id, port_name, |
|
n_const.DEVICE_OWNER_DHCP, None, None, |
|
None, segments) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', 'network id net-id', |
|
'dhcp id vm-1 hostid host port-id 123 name "123-port"', |
|
] |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_unplug_host_from_network(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
vm_id = 'vm-1' |
|
port_id = 123 |
|
network_id = 'net-id' |
|
host = 'host' |
|
self.drv.unplug_host_from_network(vm_id, host, port_id, |
|
network_id, tenant_id) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', 'vm id vm-1 hostid host', |
|
'no port id 123', |
|
] |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_unplug_dhcp_port_from_network(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
vm_id = 'vm-1' |
|
port_id = 123 |
|
network_id = 'net-id' |
|
host = 'host' |
|
|
|
self.drv.unplug_dhcp_port_from_network(vm_id, host, port_id, |
|
network_id, tenant_id) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', 'network id net-id', |
|
'no dhcp id vm-1 port-id 123', |
|
] |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_create_network(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
self.drv.cli_commands['features'] = {'hierarchical-port-binding': 1} |
|
network = { |
|
'network_id': 'net-id', |
|
'network_name': 'net-name', |
|
'segments': [{'segmentation_id': 123, |
|
'physical_network': 'default', |
|
'network_type': 'vlan', |
|
'id': 'segment_id_1'}], |
|
'shared': False, |
|
} |
|
self.drv.create_network(tenant_id, network) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', 'network id net-id name "net-name"', |
|
] |
|
for seg in network['segments']: |
|
is_dynamic = seg.get('is_dynamic', False) |
|
cmd2.append('segment %s type %s id %d %s' % (seg['id'], |
|
seg['network_type'], seg['segmentation_id'], |
|
'dynamic' if is_dynamic else 'static')) |
|
cmd2.append('no shared') |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_create_shared_network(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
segment_id = 'abcd-cccc' |
|
segmentation_id = 123 |
|
network_type = 'vlan' |
|
segments = [{'segmentation_id': segmentation_id, |
|
'id': segment_id, |
|
'network_type': network_type}] |
|
network = { |
|
'network_id': 'net-id', |
|
'network_name': 'net-name', |
|
'segments': segments, |
|
'shared': True} |
|
self.drv.cli_commands['features'] = {'hierarchical-port-binding': 1} |
|
self.drv.create_network(tenant_id, network) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', 'network id net-id name "net-name"', |
|
'segment %s type %s id %d %s' % (segment_id, network_type, |
|
segmentation_id, 'static'), |
|
'shared', |
|
] |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_create_network_bulk(self, mock_send_eapi_req): |
|
tenant_id = 'ten-2' |
|
num_networks = 10 |
|
network_type = 'vlan' |
|
segment_id = 'abcd-eeee-%s' |
|
self.drv.cli_commands['features'] = {'hierarchical-port-binding': 1} |
|
networks = [{ |
|
'network_id': 'net-id-%d' % net_id, |
|
'network_name': 'net-name-%d' % net_id, |
|
'segments': [{'segmentation_id': net_id, |
|
'network_type': 'vlan', |
|
'id': segment_id % net_id}], |
|
'shared': True, |
|
} for net_id in range(1, num_networks) |
|
] |
|
|
|
self.drv.create_network_bulk(tenant_id, networks) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne', |
|
'tenant ten-2'] |
|
for net_id in range(1, num_networks): |
|
cmd2.append('network id net-id-%d name "net-name-%d"' % |
|
(net_id, net_id)) |
|
cmd2.append('segment %s type %s id %d %s' % ( |
|
segment_id % net_id, network_type, net_id, 'static')) |
|
cmd2.append('shared') |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_network(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
network_id = 'net-id' |
|
segments = [{'segmentation_id': 101, |
|
'physical_network': 'default', |
|
'id': 'segment_id_1', |
|
'network_type': 'vlan'}] |
|
self.drv.delete_network(tenant_id, network_id, segments) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', |
|
'network id net-id', |
|
'no segment segment_id_1', |
|
] |
|
cmd3 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', |
|
'no network id net-id', |
|
] |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, |
|
[cmd1, cmd2, cmd1, cmd3]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_network_bulk(self, mock_send_eapi_req): |
|
tenant_id = 'ten-2' |
|
num_networks = 10 |
|
|
|
networks = ['net-id-%d' % net_id for net_id in range(1, num_networks)] |
|
self.drv.delete_network_bulk(tenant_id, networks) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne', |
|
'tenant ten-2'] |
|
for net_id in range(1, num_networks): |
|
cmd2.append('no network id net-id-%d' % net_id) |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_vm(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
vm_id = 'vm-id' |
|
self.drv.delete_vm(tenant_id, vm_id) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', |
|
'tenant ten-1', 'no vm id vm-id', |
|
] |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_vm_bulk(self, mock_send_eapi_req): |
|
tenant_id = 'ten-2' |
|
num_vms = 10 |
|
vm_ids = ['vm-id-%d' % vm_id for vm_id in range(1, num_vms)] |
|
self.drv.delete_vm_bulk(tenant_id, vm_ids) |
|
|
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne', |
|
'tenant ten-2'] |
|
|
|
for vm_id in range(1, num_vms): |
|
cmd2.append('no vm id vm-id-%d' % vm_id) |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_create_port_bulk(self, mock_send_eapi_req): |
|
tenant_id = 'ten-3' |
|
num_devices = 10 |
|
num_ports_per_device = 2 |
|
|
|
device_count = 0 |
|
devices = {} |
|
for device_id in range(1, num_devices): |
|
device_count += 1 |
|
dev_id = 'dev-id-%d' % device_id |
|
devices[dev_id] = {'vmId': dev_id, |
|
'baremetal_instance': False, |
|
'ports': [] |
|
} |
|
for port_id in range(1, num_ports_per_device): |
|
port_id = 'port-id-%d-%d' % (device_id, port_id) |
|
port = { |
|
'device_id': 'dev-id-%d' % device_id, |
|
'hosts': ['host_%d' % (device_count)], |
|
'portId': port_id |
|
} |
|
devices[dev_id]['ports'].append(port) |
|
|
|
device_owners = [n_const.DEVICE_OWNER_DHCP, 'compute', |
|
n_const.DEVICE_OWNER_DVR_INTERFACE] |
|
port_list = [] |
|
|
|
net_count = 1 |
|
for device_id in range(1, num_devices): |
|
for port_id in range(1, num_ports_per_device): |
|
port = { |
|
'portId': 'port-id-%d-%d' % (device_id, port_id), |
|
'device_id': 'dev-id-%d' % device_id, |
|
'device_owner': device_owners[(device_id + port_id) % 3], |
|
'network_id': 'network-id-%d' % net_count, |
|
'name': 'port-%d-%d' % (device_id, port_id), |
|
'tenant_id': tenant_id |
|
} |
|
port_list.append(port) |
|
net_count += 1 |
|
|
|
create_ports = {} |
|
port_profiles = {} |
|
for port in port_list: |
|
create_ports.update(port_dict_representation(port)) |
|
port_profiles[port['portId']] = {'vnic_type': 'normal'} |
|
|
|
self.drv.cli_commands[arista_ml2.CMD_INSTANCE] = 'instance' |
|
self.drv.create_instance_bulk(tenant_id, create_ports, devices, |
|
port_profiles=port_profiles) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne', |
|
'tenant ten-3'] |
|
|
|
for device in devices.values(): |
|
for v_port in device['ports']: |
|
port_id = v_port['portId'] |
|
port = create_ports[port_id] |
|
host = v_port['hosts'][0] |
|
device_owner = port['device_owner'] |
|
port_name = port['name'] |
|
network_id = port['network_id'] |
|
device_id = port['device_id'] |
|
if device_owner == 'network:dhcp': |
|
cmd2.append('network id %s' % network_id) |
|
cmd2.append('dhcp id %s hostid %s port-id %s name "%s"' % ( |
|
device_id, host, port_id, port_name)) |
|
elif device_owner == 'compute': |
|
cmd2.append('vm id %s hostid %s' % (device_id, host)) |
|
cmd2.append('port id %s name "%s" network-id %s' % ( |
|
port_id, port_name, network_id)) |
|
elif device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE: |
|
cmd2.append('instance id %s type router' % device_id) |
|
cmd2.append('port id %s network-id %s hostid %s' % ( |
|
port_id, network_id, host)) |
|
net_count += 1 |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_tenant(self, mock_send_eapi_req): |
|
tenant_id = 'ten-1' |
|
self.drv.delete_tenant(tenant_id) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', 'configure', 'cvx', 'service openstack', |
|
'region RegionOne', 'no tenant ten-1', |
|
] |
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_tenant_bulk(self, mock_send_eapi_req): |
|
num_tenants = 10 |
|
tenant_list = ['ten-%d' % t_id for t_id in range(1, num_tenants)] |
|
self.drv.delete_tenant_bulk(tenant_list) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne'] |
|
for ten_id in range(1, num_tenants): |
|
cmd2.append('no tenant ten-%d' % ten_id) |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
def test_get_network_info_returns_none_when_no_such_net(self): |
|
expected = [] |
|
self.drv.get_tenants = mock.MagicMock() |
|
self.drv.get_tenants.return_value = [] |
|
|
|
net_info = self.drv.get_tenants() |
|
|
|
self.drv.get_tenants.assert_called_once_with() |
|
self.assertEqual(net_info, expected, ('Network info must be "None"' |
|
'for unknown network')) |
|
|
|
def test_get_network_info_returns_info_for_available_net(self): |
|
valid_network_id = '12345' |
|
valid_net_info = {'network_id': valid_network_id, |
|
'some_info': 'net info'} |
|
known_nets = valid_net_info |
|
|
|
self.drv.get_tenants = mock.MagicMock() |
|
self.drv.get_tenants.return_value = known_nets |
|
|
|
net_info = self.drv.get_tenants() |
|
self.assertEqual(net_info, valid_net_info, |
|
('Must return network info for a valid net')) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_check_supported_features(self, mock_send_eapi_req): |
|
self.drv._get_random_name = mock.MagicMock() |
|
self.drv._get_random_name.return_value = 'RegionOne' |
|
|
|
self.drv.check_supported_features() |
|
|
|
get_eos_master_cmd = ['show openstack agent uuid'] |
|
instance_command = ['show openstack instances'] |
|
cmds = [get_eos_master_cmd, instance_command] |
|
|
|
calls = [] |
|
calls.extend(mock.call(cmds=cmd, commands_to_log=cmd) for cmd in cmds) |
|
mock_send_eapi_req.assert_has_calls(calls) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_register_with_eos(self, mock_send_eapi_req): |
|
self.drv.register_with_eos() |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region %s' % self.region, |
|
'sync interval %d' % cfg.CONF.ml2_arista.sync_interval, |
|
] |
|
self._verify_send_eapi_request_calls( |
|
mock_send_eapi_req, |
|
[cmd1, cmd2], |
|
commands_to_log=[cmd1, cmd2]) |
|
|
|
def _enable_sync_cmds(self): |
|
self.drv.cli_commands[ |
|
arista_ml2.CMD_REGION_SYNC] = 'region RegionOne sync' |
|
self.drv.cli_commands[arista_ml2.CMD_SYNC_HEARTBEAT] = 'sync heartbeat' |
|
self.drv.cli_commands['baremetal'] = '' |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_create_network_bulk_during_sync(self, mock_send_eapi_req): |
|
self._enable_sync_cmds() |
|
self.drv.cli_commands['features'] = {'hierarchical-port-binding': 1} |
|
tenant_id = 'ten-10' |
|
num_networks = 101 |
|
segments = [{'segmentation_id': 101, |
|
'physical_network': 'default', |
|
'id': 'segment_id_1', |
|
'network_type': 'vlan'}] |
|
networks = [{ |
|
'network_id': 'net-id-%d' % net_id, |
|
'network_name': 'net-name-%d' % net_id, |
|
'segments': segments, |
|
'shared': True, |
|
} for net_id in range(1, num_networks + 1) |
|
] |
|
|
|
self.drv.create_network_bulk(tenant_id, networks, sync=True) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne sync', |
|
'tenant ten-10'] |
|
|
|
# Send 100 create network commands |
|
for net_id in range(1, 101): |
|
cmd2.append('network id net-id-%d name "net-name-%d"' % |
|
(net_id, net_id)) |
|
for seg in segments: |
|
is_dynamic = seg.get('is_dynamic', False) |
|
cmd2.append('segment %s type %s id %d %s' % (seg['id'], |
|
seg['network_type'], seg['segmentation_id'], |
|
'dynamic' if is_dynamic else 'static')) |
|
cmd2.append('shared') |
|
|
|
# Send heartbeat |
|
cmd2.append('sync heartbeat') |
|
# Send the remaining network |
|
cmd2.append('network id net-id-101 name "net-name-101"') |
|
for seg in segments: |
|
is_dynamic = seg.get('is_dynamic', False) |
|
cmd2.append('segment %s type %s id %d %s' % (seg['id'], |
|
seg['network_type'], seg['segmentation_id'], |
|
'dynamic' if is_dynamic else 'static')) |
|
cmd2.append('shared') |
|
# Send the final heartbeat |
|
cmd2.append('sync heartbeat') |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_network_bulk_during_sync(self, mock_send_eapi_req): |
|
self._enable_sync_cmds() |
|
tenant_id = 'ten-10' |
|
num_networks = 101 |
|
networks = ['nid-%d' % net_id for net_id in range(1, num_networks + 1)] |
|
|
|
self.drv.delete_network_bulk(tenant_id, networks, sync=True) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne sync', |
|
'tenant ten-10'] |
|
|
|
# Send 100 create network commands |
|
for net_id in range(1, 101): |
|
cmd2.append('no network id nid-%d' % (net_id)) |
|
|
|
# Send heartbeat |
|
cmd2.append('sync heartbeat') |
|
# Send the remaining network |
|
cmd2.append('no network id nid-101') |
|
# Send the final heartbeat |
|
cmd2.append('sync heartbeat') |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_vm_bulk_during_sync(self, mock_send_eapi_req): |
|
self._enable_sync_cmds() |
|
tenant_id = 'ten-2' |
|
num_vms = 101 |
|
vm_ids = ['vm-id-%d' % vm_id for vm_id in range(1, num_vms + 1)] |
|
self.drv.delete_vm_bulk(tenant_id, vm_ids, sync=True) |
|
|
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne sync', |
|
'tenant ten-2'] |
|
|
|
for vm_id in range(1, 101): |
|
cmd2.append('no vm id vm-id-%d' % vm_id) |
|
|
|
# Send heartbeat |
|
cmd2.append('sync heartbeat') |
|
# Send the remaining vm |
|
cmd2.append('no vm id vm-id-101') |
|
# Send the final heartbeat |
|
cmd2.append('sync heartbeat') |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_create_port_bulk_during_sync(self, mock_send_eapi_req): |
|
self._enable_sync_cmds() |
|
tenant_id = 'ten-3' |
|
num_devices = 101 |
|
num_ports_per_device = 2 |
|
|
|
device_count = 0 |
|
devices = {} |
|
for device_id in range(1, num_devices): |
|
device_count += 1 |
|
dev_id = 'dev-id-%d' % device_id |
|
devices[dev_id] = {'vmId': dev_id, |
|
'baremetal_instance': False, |
|
'ports': [] |
|
} |
|
for port_id in range(1, num_ports_per_device + 1): |
|
port_id = 'port-id-%d-%d' % (device_id, port_id) |
|
port = { |
|
'device_id': 'dev-id-%d' % device_id, |
|
'hosts': ['host_%d' % (device_count)], |
|
'portId': port_id |
|
} |
|
devices[dev_id]['ports'].append(port) |
|
|
|
device_owners = [n_const.DEVICE_OWNER_DHCP, 'compute', |
|
n_const.DEVICE_OWNER_DVR_INTERFACE] |
|
|
|
port_list = [] |
|
net_count = 1 |
|
for device_id in range(1, num_devices): |
|
for port_id in range(1, num_ports_per_device + 1): |
|
port = { |
|
'portId': 'port-id-%d-%d' % (device_id, port_id), |
|
'device_id': 'dev-id-%d' % device_id, |
|
'device_owner': device_owners[(device_id + port_id) % 3], |
|
'network_id': 'network-id-%d' % net_count, |
|
'name': 'port-%d-%d' % (device_id, port_id), |
|
'tenant_id': tenant_id |
|
} |
|
port_list.append(port) |
|
net_count += 1 |
|
|
|
create_ports = {} |
|
port_profiles = {} |
|
for port in port_list: |
|
create_ports.update(port_dict_representation(port)) |
|
port_profiles[port['portId']] = {'vnic_type': 'normal'} |
|
|
|
self.drv.cli_commands[arista_ml2.CMD_INSTANCE] = 'instance' |
|
self.drv.create_instance_bulk(tenant_id, create_ports, devices, |
|
port_profiles=port_profiles, sync=True) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne sync', |
|
'tenant ten-3'] |
|
|
|
for count, device in enumerate(devices.values(), 1): |
|
for v_port in device['ports']: |
|
port_id = v_port['portId'] |
|
port = create_ports[port_id] |
|
host = v_port['hosts'][0] |
|
vm_id = device['vmId'] |
|
port_name = port['name'] |
|
network_id = port['network_id'] |
|
device_owner = port['device_owner'] |
|
device_id = port['device_id'] |
|
|
|
if device_owner == n_const.DEVICE_OWNER_DHCP: |
|
cmd2.append('network id %s' % network_id) |
|
cmd2.append('dhcp id %s hostid %s port-id %s name "%s"' % ( |
|
vm_id, host, port_id, port_name)) |
|
elif device_owner == 'compute': |
|
cmd2.append('vm id %s hostid %s' % (vm_id, host)) |
|
cmd2.append('port id %s name "%s" network-id %s' % ( |
|
port_id, port_name, network_id)) |
|
elif device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE: |
|
cmd2.append('instance id %s type router' % device_id) |
|
cmd2.append('port id %s network-id %s hostid %s' % ( |
|
port_id, network_id, host)) |
|
if count == (num_devices - 1): |
|
# Send heartbeat |
|
cmd2.append('sync heartbeat') |
|
|
|
# Send the final heartbeat |
|
cmd2.append('sync heartbeat') |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
@patch(EAPI_SEND_FUNC) |
|
def test_delete_tenant_bulk_during_sync(self, mock_send_eapi_req): |
|
self._enable_sync_cmds() |
|
num_tenants = 101 |
|
tenant_list = ['ten-%d' % t_id for t_id in range(1, num_tenants + 1)] |
|
self.drv.delete_tenant_bulk(tenant_list, sync=True) |
|
cmd1 = ['show openstack agent uuid'] |
|
cmd2 = ['enable', |
|
'configure', |
|
'cvx', |
|
'service openstack', |
|
'region RegionOne sync'] |
|
for ten_id in range(1, num_tenants + 1): |
|
cmd2.append('no tenant ten-%d' % ten_id) |
|
|
|
cmd2.append('sync heartbeat') |
|
|
|
self._verify_send_eapi_request_calls(mock_send_eapi_req, [cmd1, cmd2]) |
|
|
|
|
|
class AristaRPCWrapperInvalidConfigTestCase(base.BaseTestCase): |
|
"""Negative test cases to test the Arista Driver configuration.""" |
|
|
|
def setUp(self): |
|
super(AristaRPCWrapperInvalidConfigTestCase, self).setUp() |
|
self.setup_invalid_config() # Invalid config, required options not set |
|
|
|
def setup_invalid_config(self): |
|
utils.setup_arista_wrapper_config(cfg, host='', user='') |
|
|
|
def test_raises_exception_on_wrong_configuration(self): |
|
ndb = db_lib.NeutronNets() |
|
self.assertRaises(arista_exc.AristaConfigError, |
|
arista_ml2.AristaRPCWrapperEapi, ndb) |
|
|
|
|
|
class NegativeRPCWrapperTestCase(testlib_api.SqlTestCase): |
|
"""Negative test cases to test the RPC between Arista Driver and EOS.""" |
|
|
|
def setUp(self): |
|
super(NegativeRPCWrapperTestCase, self).setUp() |
|
setup_valid_config() |
|
|
|
def test_exception_is_raised_on_json_server_error(self): |
|
ndb = db_lib.NeutronNets() |
|
drv = arista_ml2.AristaRPCWrapperEapi(ndb) |
|
|
|
drv._send_api_request = mock.MagicMock( |
|
side_effect=Exception('server error') |
|
) |
|
with mock.patch.object(arista_ml2.LOG, 'error') as log_err: |
|
self.assertRaises(arista_exc.AristaRpcError, drv.get_tenants) |
|
log_err.assert_called_once_with(mock.ANY) |
|
|
|
|
|
class RealNetStorageAristaDriverTestCase(testlib_api.SqlTestCase): |
|
"""Main test cases for Arista Mechanism driver. |
|
|
|
Tests all mechanism driver APIs supported by Arista Driver. It invokes |
|
all the APIs as they would be invoked in real world scenarios and |
|
verifies the functionality. |
|
""" |
|
def setUp(self): |
|
super(RealNetStorageAristaDriverTestCase, self).setUp() |
|
setup_valid_config() |
|
self.fake_rpc = mock.MagicMock() |
|
self.drv = mechanism_arista.AristaDriver(self.fake_rpc) |
|
|
|
def tearDown(self): |
|
super(RealNetStorageAristaDriverTestCase, self).tearDown() |
|
|
|
def test_create_and_delete_network(self): |
|
tenant_id = 'ten-1' |
|
network_id = 'net1-id' |
|
segmentation_id = 1001 |
|
|
|
network_context = self._get_network_context(tenant_id, |
|
network_id, |
|
segmentation_id) |
|
self.drv.create_network_precommit(network_context) |
|
net_provisioned = db_lib.is_network_provisioned(tenant_id, network_id) |
|
self.assertTrue(net_provisioned, 'The network should be created') |
|
|
|
expected_num_nets = 1 |
|
num_nets_provisioned = db_lib.num_nets_provisioned(tenant_id) |
|
self.assertEqual(expected_num_nets, num_nets_provisioned, |
|
'There should be %d nets, not %d' % |
|
(expected_num_nets, num_nets_provisioned)) |
|
|
|
# Now test the delete network |
|
self.drv.delete_network_precommit(network_context) |
|
net_provisioned = db_lib.is_network_provisioned(tenant_id, network_id) |
|
self.assertFalse(net_provisioned, 'The network should be created') |
|
|
|
expected_num_nets = 0 |
|
num_nets_provisioned = db_lib.num_nets_provisioned(tenant_id) |
|
self.assertEqual(expected_num_nets, num_nets_provisioned, |
|
'There should be %d nets, not %d' % |
|
(expected_num_nets, num_nets_provisioned)) |
|
|
|
def test_create_and_delete_multiple_networks(self): |
|
tenant_id = 'ten-1' |
|
expected_num_nets = 100 |
|
segmentation_id = 1001 |
|
nets = ['id%s' % n for n in range(expected_num_nets)] |
|
for net_id in nets: |
|
network_context = self._get_network_context(tenant_id, |
|
net_id, |
|
segmentation_id) |
|
self.drv.create_network_precommit(network_context) |
|
|
|
num_nets_provisioned = db_lib.num_nets_provisioned(tenant_id) |
|
self.assertEqual(expected_num_nets, num_nets_provisioned, |
|
'There should be %d nets, not %d' % |
|
(expected_num_nets, num_nets_provisioned)) |
|
|
|
# Now test the delete networks |
|
for net_id in nets: |
|
network_context = self._get_network_context(tenant_id, |
|
net_id, |
|
segmentation_id) |
|
self.drv.delete_network_precommit(network_context) |
|
|
|
num_nets_provisioned = db_lib.num_nets_provisioned(tenant_id) |
|
expected_num_nets = 0 |
|
self.assertEqual(expected_num_nets, num_nets_provisioned, |
|
'There should be %d nets, not %d' % |
|
(expected_num_nets, num_nets_provisioned)) |
|
|
|
def test_create_and_delete_ports(self): |
|
tenant_id = 'ten-1' |
|
network_id = 'net1-id' |
|
segmentation_id = 1001 |
|
vms = ['vm1', 'vm2', 'vm3'] |
|
|
|
network_context = self._get_network_context(tenant_id, |
|
network_id, |
|
segmentation_id) |
|
self.drv.create_network_precommit(network_context) |
|
|
|
for vm_id in vms: |
|
port_id = '%s_%s' % (vm_id, 101) |
|
port_context = self._get_port_context(port_id, |
|
tenant_id, |
|
network_id, |
|
vm_id, |
|
network_context) |
|
self.drv.update_port_precommit(port_context) |
|
|
|
vm_list = db_lib.get_vms(tenant_id) |
|
provisioned_vms = len(vm_list) |
|
expected_vms = len(vms) |
|
self.assertEqual(expected_vms, provisioned_vms, |
|
'There should be %d ' |
|
'hosts, not %d' % (expected_vms, provisioned_vms)) |
|
|
|
# Now test the delete ports |
|
for vm_id in vms: |
|
port_id = '%s_%s' % (vm_id, 101) |
|
port_context = self._get_port_context(port_id, |
|
tenant_id, |
|
network_id, |
|
vm_id, |
|
network_context) |
|
self.drv.delete_port_precommit(port_context) |
|
|
|
vm_list = db_lib.get_vms(tenant_id) |
|
provisioned_vms = len(vm_list) |
|
expected_vms = 0 |
|
self.assertEqual(expected_vms, provisioned_vms, |
|
'There should be %d ' |
|
'VMs, not %d' % (expected_vms, provisioned_vms)) |
|
|
|
def test_cleanup_on_start(self): |
|
"""Ensures that the driver cleans up the arista database on startup.""" |
|
ndb = db_lib.NeutronNets() |
|
|
|
# create a shared session |
|
session = db_api.get_writer_session() |
|
|
|
# Create some networks in neutron db |
|
n1_context = self._get_network_context('t1', 'n1', 10, session) |
|
ndb.create_network(n1_context, {'network': n1_context.current}) |
|
n2_context = self._get_network_context('t2', 'n2', 20, session) |
|
ndb.create_network(n2_context, {'network': n2_context.current}) |
|
n3_context = self._get_network_context('', 'ha-network', 100, session) |
|
ndb.create_network(n3_context, {'network': n3_context.current}) |
|
|
|
# Objects were created in different sessions, but Neutron no longer |
|
# implicitly flushes subtransactions. |
|
session.flush() |
|
|
|
# Create some networks in Arista db |
|
db_lib.remember_network_segment('t1', 'n1', 10, 'segment_id_10') |
|
db_lib.remember_network_segment('t2', 'n2', 20, 'segment_id_20') |
|
db_lib.remember_network_segment('admin', |
|
'ha-network', 100, 'segment_id_100') |
|
db_lib.remember_network_segment('t3', 'n3', 30, 'segment_id_30') |
|
|
|
# Initialize the driver which should clean up the extra networks |
|
self.drv.initialize() |
|
|
|
worker = self.drv.get_workers()[0] |
|
|
|
with mock.patch.object(worker.sync_service, 'do_synchronize') as ds: |
|
worker.start() |
|
adb_networks = db_lib.get_networks(tenant_id='any') |
|
|
|
# 'n3' should now be deleted from the Arista DB |
|
self.assertEqual( |
|
set(('n1', 'n2', 'ha-network')), |
|
set(adb_networks.keys()) |
|
) |
|
|
|
ds.assert_called_once_with() |
|
|
|
def _get_network_context(self, tenant_id, net_id, seg_id, session=None): |
|
network = {'id': net_id, |
|
'tenant_id': tenant_id, |
|
'name': net_id, |
|
'admin_state_up': True, |
|
'shared': False, |
|
} |
|
network_segments = [{'segmentation_id': seg_id, |
|
'id': 'segment_%s' % net_id, |
|
'network_type': 'vlan'}] |
|
return FakeNetworkContext(network, network_segments, network, session) |
|
|
|
def _get_port_context(self, port_id, tenant_id, net_id, vm_id, network): |
|
port = {'device_id': vm_id, |
|
'device_owner': 'compute', |
|
'binding:host_id': 'ubuntu1', |
|
'binding:vnic_type': 'normal', |
|
'tenant_id': tenant_id, |
|
'id': port_id, |
|
'network_id': net_id, |
|
'name': '', |
|
'status': 'ACTIVE', |
|
} |
|
binding_levels = [] |
|
for level, segment in enumerate(network.network_segments): |
|
binding_levels.append(FakePortBindingLevel(port['id'], |
|
level, |
|
'vendor-1', |
|
segment['id'])) |
|
return FakePortContext(port, port, network, port['status'], |
|
binding_levels) |
|
|
|
|
|
class FakeNetworkContext(object): |
|
"""To generate network context for testing purposes only.""" |
|
|
|
def __init__(self, network, segments=None, original_network=None, |
|
session=None): |
|
self._network = network |
|
self._original_network = original_network |
|
self._segments = segments |
|
self.is_admin = False |
|
self.tenant_id = network['tenant_id'] |
|
self.session = session or db_api.get_reader_session() |
|
|
|
@property |
|
def current(self): |
|
return self._network |
|
|
|
@property |
|
def original(self): |
|
return self._original_network |
|
|
|
@property |
|
def network_segments(self): |
|
return self._segments |
|
|
|
|
|
class FakePluginContext(object): |
|
"""Plugin context for testing purposes only.""" |
|
|
|
def __init__(self, tenant_id): |
|
self.tenant_id = tenant_id |
|
self.session = mock.MagicMock() |
|
|
|
|
|
class FakePortContext(object): |
|
"""To generate port context for testing purposes only.""" |
|
|
|
def __init__(self, port, original_port, network, status, |
|
binding_levels): |
|
self._plugin_context = None |
|
self._port = port |
|
self._original_port = original_port |
|
self._network_context = network |
|
self._status = status |
|
self._binding_levels = binding_levels |
|
|
|
@property |
|
def current(self): |
|
return self._port |
|
|
|
@property |
|
def original(self): |
|
return self._original_port |
|
|
|
@property |
|
def network(self): |
|
return self._network_context |
|
|
|
@property |
|
def host(self): |
|
return self._port.get(portbindings.HOST_ID) |
|
|
|
@property |
|
def original_host(self): |
|
return self._original_port.get(portbindings.HOST_ID) |
|
|
|
@property |
|
def status(self): |
|
return self._status |
|
|
|
@property |
|
def original_status(self): |
|
if self._original_port: |
|
return self._original_port['status'] |
|
|
|
@property |
|
def binding_levels(self): |
|
if self._binding_levels: |
|
return [{ |
|
driver_api.BOUND_DRIVER: level.driver, |
|
driver_api.BOUND_SEGMENT: |
|
self._expand_segment(level.segment_id) |
|
} for level in self._binding_levels] |
|
|
|
@property |
|
def bottom_bound_segment(self): |
|
if self._binding_levels: |
|
return self._expand_segment(self._binding_levels[-1].segment_id) |
|
|
|
def _expand_segment(self, segment_id): |
|
for segment in self._network_context.network_segments: |
|
if segment[driver_api.ID] == segment_id: |
|
return segment |
|
|
|
|
|
class FakePortBindingLevel(object): |
|
"""Port binding object for testing purposes only.""" |
|
|
|
def __init__(self, port_id, level, driver, segment_id): |
|
self.port_id = port_id |
|
self.level = level |
|
self.driver = driver |
|
self.segment_id = segment_id |
|
|
|
|
|
class SyncServiceTest(testlib_api.SqlTestCase): |
|
"""Test cases for the sync service.""" |
|
|
|
def setUp(self): |
|
super(SyncServiceTest, self).setUp() |
|
plugin_klass = importutils.import_class( |
|
"neutron.db.db_base_plugin_v2.NeutronDbPluginV2") |
|
directory.add_plugin(plugin_constants.CORE, plugin_klass()) |
|
self.rpc = mock.MagicMock() |
|
ndb = db_lib.NeutronNets() |
|
self.sync_service = arista_ml2.SyncService(self.rpc, ndb) |
|
self.sync_service._force_sync = False |
|
|
|
def test_region_in_sync(self): |
|
"""Tests whether the region_in_sync() behaves as expected.""" |
|
region_updated_time = { |
|
'regionName': 'RegionOne', |
|
'regionTimestamp': '12345' |
|
} |
|
self.rpc.get_region_updated_time.return_value = region_updated_time |
|
self.sync_service._region_updated_time = None |
|
assert not self.sync_service._region_in_sync() |
|
self.sync_service._region_updated_time = region_updated_time |
|
assert self.sync_service._region_in_sync() |
|
|
|
def test_synchronize_required(self): |
|
"""Tests whether synchronize() sends the right commands. |
|
|
|
This test verifies a scenario when the sync is required. |
|
""" |
|
region_updated_time = { |
|
'regionName': 'RegionOne', |
|
'regionTimestamp': '12345' |
|
} |
|
self.rpc.get_region_updated_time.return_value = region_updated_time |
|
self.sync_service._region_updated_time = { |
|
'regionName': 'RegionOne', |
|
'regionTimestamp': '0', |
|
} |
|
|
|
tenant_id = 'tenant-1' |
|
network_id = 'net-1' |
|
segmentation_id = 42 |
|
segment_id = 'segment_id_1' |
|
db_lib.remember_tenant(tenant_id) |
|
db_lib.remember_network_segment(tenant_id, network_id, segmentation_id, |
|
segment_id) |
|
|
|
self.rpc.get_tenants.return_value = {} |
|
|
|
self.rpc.sync_start.return_value = True |
|
self.rpc.sync_end.return_value = True |
|
self.rpc.check_cvx_availability.return_value = True |
|
|
|
self.rpc._baremetal_supported.return_value = False |
|
self.rpc.get_all_baremetal_hosts.return_value = {} |
|
|
|
self.sync_service.do_synchronize() |
|
|
|
expected_calls = [ |
|
mock.call.perform_sync_of_sg(), |
|
mock.call.check_cvx_availability(), |
|
mock.call.get_region_updated_time(), |
|
mock.call.sync_start(), |
|
mock.call.register_with_eos(sync=True), |
|
mock.call.check_supported_features(), |
|
mock.call.get_tenants(), |
|
mock.call.create_network_bulk( |
|
tenant_id, |
|
[{'network_id': network_id, |
|
'segments': [], |
|
'network_name': '', |
|
'shared': False}], |
|
sync=True), |
|
mock.call.sync_end(), |
|
mock.call.get_region_updated_time() |
|
] |
|
self.assertTrue(self.rpc.mock_calls == expected_calls, |
|
"Seen: %s\nExpected: %s" % ( |
|
self.rpc.mock_calls, |
|
expected_calls, |
|
) |
|
) |
|
|
|
db_lib.forget_network_segment(tenant_id, network_id) |
|
db_lib.forget_tenant(tenant_id) |
|
|
|
def test_synchronize_not_required(self): |
|
"""Tests whether synchronize() sends the right commands. |
|
|
|
This test verifies a scenario when the sync is not required. |
|
""" |
|
region_updated_time = { |
|
'regionName': 'RegionOne', |
|
'regionTimestamp': '424242' |
|
} |
|
self.rpc.get_region_updated_time.return_value = region_updated_time |
|
self.rpc.check_cvx_availability.return_value = True |
|
self.sync_service._region_updated_time = { |
|
'regionName': 'RegionOne', |
|
'regionTimestamp': '424242', |
|
} |
|
|
|
self.rpc.sync_start.return_value = True |
|
self.rpc.sync_end.return_value = True |
|
|
|
self.sync_service.do_synchronize() |
|
|
|
# If the timestamps do match, then the sync should not be executed. |
|
expected_calls = [ |
|
mock.call.perform_sync_of_sg(), |
|
mock.call.check_cvx_availability(), |
|
mock.call.get_region_updated_time(), |
|
] |
|
self.assertTrue(self.rpc.mock_calls[:4] == expected_calls, |
|
"Seen: %s\nExpected: %s" % ( |
|
self.rpc.mock_calls, |
|
expected_calls, |
|
|