Skip to content

Commit

Permalink
Handle External IPAM cases
Browse files Browse the repository at this point in the history
Create Instance IP for external IPAM
Remove unused functions, tests and imports

Closes-Bug: #1779896
Change-Id: Ic69f7b9ddf1169130695dc30b446bf677493dc40
  • Loading branch information
aszc-dev committed Jul 4, 2018
1 parent f17be8d commit 9f11bf0
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 161 deletions.
6 changes: 5 additions & 1 deletion cvm/clients.py
Expand Up @@ -448,12 +448,16 @@ def __init__(self):
def add_port(self, vmi_model):
""" Add port to VRouter Agent. """
try:
ip_address = vmi_model.ip_address
if vmi_model.vnc_instance_ip:
ip_address = vmi_model.vnc_instance_ip.instance_ip_address

parameters = dict(
vm_uuid_str=vmi_model.vm_model.uuid,
vif_uuid_str=vmi_model.uuid,
interface_name=vmi_model.uuid,
mac_address=vmi_model.vcenter_port.mac_address,
ip_address=vmi_model.vnc_instance_ip.instance_ip_address,
ip_address=ip_address,
vn_id=vmi_model.vn_model.uuid,
display_name=vmi_model.vm_model.name,
vlan=vmi_model.vcenter_port.vlan_id,
Expand Down
5 changes: 0 additions & 5 deletions cvm/database.py
Expand Up @@ -89,8 +89,3 @@ def delete_vmi_model(self, uuid):
self.vmi_models.pop(uuid)
except KeyError:
logger.info('Could not find VMI model with uuid %s. Nothing to delete.', uuid)

def print_out(self):
print self.vm_models
print self.vn_models
print self.vmi_models
62 changes: 11 additions & 51 deletions cvm/models.py
@@ -1,4 +1,3 @@
import ipaddress
import logging
import uuid
from collections import deque
Expand All @@ -17,27 +16,6 @@
enable=True)


def find_virtual_machine_ip_address(vmware_vm, port_group_name):
try:
return next(
addr for nicInfo in vmware_vm.guest.net
if is_nic_info_valid(nicInfo)
for addr in nicInfo.ipAddress
if (nicInfo.network == port_group_name and
is_ipv4(addr.decode('utf-8')))
)
except (StopIteration, AttributeError):
return None


def is_ipv4(string):
return isinstance(ipaddress.ip_address(string), ipaddress.IPv4Address)


def is_nic_info_valid(info):
return hasattr(info, 'ipAddress') and hasattr(info, 'network')


def find_vrouter_uuid(host):
try:
for vmware_vm in host.vm:
Expand All @@ -48,20 +26,6 @@ def find_vrouter_uuid(host):
return None


def find_vm_mac_address(vmware_vm, portgroup_key):
try:
devices = vmware_vm.config.hardware.device
for device in devices:
try:
if device.backing.port.portgroupKey == portgroup_key:
return device.macAddress
except AttributeError:
pass
except AttributeError:
pass
return None


def find_vmi_port_key(vmware_vm, mac_address):
try:
devices = vmware_vm.config.hardware.device
Expand Down Expand Up @@ -167,6 +131,14 @@ def name(self):
def uuid(self):
return str(self.vnc_vn.uuid)

@property
def has_external_ipam(self):
return self.vnc_vn.get_external_ipam()

@property
def subnet_info_is_set(self):
return self.vnc_vn.get_network_ipam_refs()

@staticmethod
def get_fq_name(name):
return [VNC_ROOT_DOMAIN, VNC_VCENTER_PROJECT, name]
Expand All @@ -175,9 +147,6 @@ def get_fq_name(name):
def get_uuid(key):
return str(uuid.uuid3(uuid.NAMESPACE_DNS, key))

def subnet_info_is_set(self):
return self.vnc_vn.get_network_ipam_refs()

def __repr__(self):
return 'VirtualNetworkModel(uuid=%s, key=%s, name=%s)' % \
(self.uuid, self.key, self.name)
Expand Down Expand Up @@ -239,9 +208,7 @@ def construct_instance_ip(self):
id_perms=ID_PERMS,
)

# if self.ip_address:
# instance_ip.set_instance_ip_address(self.ip_address)
# TODO: Check if setting this to None works (remove if statement)
instance_ip.set_instance_ip_address(self.ip_address)
instance_ip.set_uuid(instance_ip_uuid)
instance_ip.set_virtual_network(self.vn_model.vnc_vn)
instance_ip.set_virtual_machine_interface(self.vnc_vmi)
Expand All @@ -251,16 +218,9 @@ def construct_instance_ip(self):
)
self.vnc_instance_ip = instance_ip

def _find_ip_address(self):
if self.vn_model.vnc_vn.get_external_ipam() and self.vm_model.tools_running:
return find_virtual_machine_ip_address(self.vm_model.vmware_vm, self.vn_model.name)
return None

def _should_construct_instance_ip(self):
return (self.vcenter_port.mac_address
and self.vn_model.subnet_info_is_set()
and (self.ip_address
or not self.vn_model.vnc_vn.get_external_ipam()))
return (self.vn_model.subnet_info_is_set
and (self.ip_address or not self.vn_model.has_external_ipam))

@staticmethod
def get_uuid(mac_address):
Expand Down
5 changes: 2 additions & 3 deletions cvm/services.py
Expand Up @@ -244,9 +244,8 @@ def update_nic(self, nic_info):
for ip in nic_info.ipAddress:
if isinstance(ipaddress.ip_address(ip.decode('utf-8')), ipaddress.IPv4Address):
vmi_model.ip_address = ip
self._update_in_vnc(vmi_model)
self._add_instance_ip_to(vmi_model)
logger.info('IP address of %s updated to %s', vmi_model.display_name, ip)
self._update_vrouter_port(vmi_model)
except AttributeError:
pass

Expand Down Expand Up @@ -332,7 +331,7 @@ def _port_needs_an_update(self, vmi_model):
vrouter_port.get('vn-id') != vmi_model.vn_model.uuid or
vrouter_port.get('rx-vlan-id') != vmi_model.vcenter_port.vlan_id or
vrouter_port.get('tx-vlan-id') != vmi_model.vcenter_port.vlan_id or
vrouter_port.get('ip-address') != vmi_model.vnc_instance_ip.instance_ip_address)
vrouter_port.get('ip-address') != vmi_model.ip_address)

def _update_port(self, vmi_model):
self._vrouter_api_client.delete_port(vmi_model.uuid)
Expand Down
78 changes: 71 additions & 7 deletions tests/test_events.py
Expand Up @@ -6,9 +6,9 @@
from vnc_api import vnc_api
from vnc_api.vnc_api import Project, VirtualNetwork

from cvm.controllers import (UpdateHandler, VmReconfiguredHandler,
VmRenamedHandler, VmUpdatedHandler,
VmwareController)
from cvm.controllers import (GuestNetHandler, UpdateHandler,
VmReconfiguredHandler, VmRenamedHandler,
VmUpdatedHandler, VmwareController)
from cvm.database import Database
from cvm.models import VirtualNetworkModel, VlanIdPool
from cvm.services import (VirtualMachineInterfaceService,
Expand Down Expand Up @@ -103,10 +103,18 @@ def assign_ip_to_instance_ip(instance_ip):
return instance_ip


def wrap_into_update_set(event):
def dont_assign_ip_to_instance_ip(instance_ip):
return instance_ip


def wrap_event_into_change(event):
change = vmodl.query.PropertyCollector.Change()
change.name = 'latestPage'
change.val = event
return change


def wrap_into_update_set(change):
update_set = vmodl.query.PropertyCollector.UpdateSet()
filter_update = vmodl.query.PropertyCollector.FilterUpdate()
object_update = vmodl.query.PropertyCollector.ObjectUpdate()
Expand All @@ -121,15 +129,17 @@ def wrap_into_update_set(event):
def vm_created_update(vmware_vm_1):
event = Mock(spec=vim.event.VmCreatedEvent())
event.vm.vm = vmware_vm_1
return wrap_into_update_set(event)
change = wrap_event_into_change(event)
return wrap_into_update_set(change)


@pytest.fixture()
def vm_renamed_update():
event = Mock(spec=vim.event.VmRenamedEvent())
event.oldName = 'VM1'
event.newName = 'VM1-renamed'
return wrap_into_update_set(event)
change = wrap_event_into_change(event)
return wrap_into_update_set(change)


@pytest.fixture()
Expand All @@ -143,7 +153,8 @@ def vm_reconfigured_update(vmware_vm_1):
device.macAddress = '11:11:11:11:11:11'
device_spec = Mock(spec=vim.vm.device.VirtualDeviceSpec(), device=device)
event.configSpec.deviceChange = [device_spec]
return wrap_into_update_set(event)
change = wrap_event_into_change(event)
return wrap_into_update_set(change)


@pytest.fixture()
Expand Down Expand Up @@ -184,6 +195,17 @@ def vlan_id_pool():
return vlan_pool


@pytest.fixture()
def nic_info_update():
nic_info = Mock(spec=vim.vm.GuestInfo.NicInfo())
nic_info.ipAddress = ['192.168.100.5']
nic_info.macAddress = '11:11:11:11:11:11'
change = Mock(spec=vmodl.query.PropertyCollector.Change())
change.name = 'guest.net'
change.val = [nic_info]
return wrap_into_update_set(change)


def assert_vmi_model_state(vmi_model, mac_address=None, ip_address=None,
vlan_id=None, display_name=None, vn_model=None, vm_model=None):
if mac_address is not None:
Expand Down Expand Up @@ -543,3 +565,45 @@ def test_contrail_vm(vcenter_api_client, vm_created_update, esxi_api_client,
# There were no calls to vrouter_api
vrouter_api_client.add_port.assert_not_called()


def test_external_ipam(vcenter_api_client, vm_created_update, esxi_api_client,
vnc_api_client, vn_model_1, nic_info_update, lock):
""" We don't need ContrailVM model for CVM to operate properly. """
vrouter_api_client = Mock()
database = Database()
vm_service = VirtualMachineService(esxi_api_client, vnc_api_client, database)
vn_service = VirtualNetworkService(esxi_api_client, vnc_api_client, database)
vmi_service = VirtualMachineInterfaceService(vcenter_api_client, vnc_api_client, database)
vrouter_port_service = VRouterPortService(vrouter_api_client, database)
vm_updated_handler = VmUpdatedHandler(vm_service, vn_service, vmi_service, vrouter_port_service)
guest_net_handler = GuestNetHandler(vmi_service, vrouter_port_service)
update_handler = UpdateHandler([vm_updated_handler, guest_net_handler])
controller = VmwareController(vm_service, vn_service, vmi_service, vrouter_port_service, update_handler, lock)

# The network we use uses external IPAM
vn_model_1.vnc_vn.external_ipam = True
database.save(vn_model_1)

# We use external IPAM, so there's no assignment of IP addresses
vnc_api_client.create_and_read_instance_ip.side_effect = dont_assign_ip_to_instance_ip

controller.handle_update(vm_created_update)

# The IP address was not assigned by Contrail Controller
vmi_model = database.get_all_vmi_models()[0]
assert vmi_model.ip_address is None
assert vmi_model.vnc_instance_ip is None

controller.handle_update(nic_info_update)

# IP address is updated
assert vmi_model.ip_address == '192.168.100.5'
assert vmi_model.vnc_instance_ip.instance_ip_address == '192.168.100.5'
assert vnc_api_client.create_and_read_instance_ip.call_args[0][0] is vmi_model.vnc_instance_ip

# vRouter port should not be updated - it will gather IP info from the Controller
vrouter_api_client.add_port.assert_called_once()
vrouter_api_client.delete_port.assert_called_once()

# The VMI itself should not be updated, since there's no new info
vnc_api_client.update_or_create_vmi.assert_called_once()
97 changes: 4 additions & 93 deletions tests/test_models.py
Expand Up @@ -5,96 +5,8 @@
from vnc_api.vnc_api import Project, SecurityGroup

from cvm.models import (ID_PERMS, VCenterPort, VirtualMachineInterfaceModel,
VirtualMachineModel, VirtualNetworkModel, VlanIdPool,
find_virtual_machine_ip_address)
from tests.utils import (create_dpg_mock, create_port_mock,
create_vmware_vm_mock)


class TestFindVirtualMachineIpAddress(TestCase):
def setUp(self):
self.vm = Mock()

def test_standard_case(self):
desired_portgroup, expected_ip = 'second', '10.7.0.60'
self.vm.guest.net = [
self._create_mock(
network='first',
ipAddress=['1.1.1.1', 'fe80::257:56ff:fe90:d265'],
),
self._create_mock(
network=desired_portgroup,
ipAddress=['fe80::250:56ff:fe90:d265', expected_ip],
),
]

result = find_virtual_machine_ip_address(self.vm, desired_portgroup)

self.assertEqual(result, expected_ip)

def test_unmatched_portgroup_name(self):
desired_portgroup, expected_ip = 'non-existent', None
self.vm.guest.net = [
self._create_mock(
network='first',
ipAddress=['1.1.1.1', 'fe80::257:56ff:fe90:d265'],
),
self._create_mock(
network='second',
ipAddress=['fe80::250:56ff:fe90:d265', expected_ip],

),
]

result = find_virtual_machine_ip_address(self.vm, desired_portgroup)

self.assertEqual(result, expected_ip)

def test_unmatched_ip_type(self):
desired_portgroup, expected_ip = 'second', None
self.vm.guest.net = [
self._create_mock(
network='first',
ipAddress=['1.1.1.1', 'fe80::257:56ff:fe90:d265'],
),
self._create_mock(
network=desired_portgroup,
ipAddress=['fe80::250:56ff:fe90:d265'],
),
]

result = find_virtual_machine_ip_address(self.vm, desired_portgroup)

self.assertEqual(result, expected_ip)

def test_missing_field(self):
desired_portgroup, expected_ip = 'irrelevant', None
self.vm.guest = None

result = find_virtual_machine_ip_address(self.vm, desired_portgroup)

self.assertEqual(result, expected_ip)

def test_missing_field_in_network(self):
desired_portgroup, expected_ip = 'second', '10.7.0.60'
self.vm.guest.net = [
None,
self._create_mock(
network=desired_portgroup,
ipAddress=['fe80::250:56ff:fe90:d265', expected_ip],
),
]

result = find_virtual_machine_ip_address(self.vm, desired_portgroup)

self.assertEqual(result, expected_ip)

@staticmethod
def _create_mock(**kwargs):
mock = Mock()
for kwarg in kwargs:
setattr(mock, kwarg, kwargs[kwarg])
return mock
VirtualMachineModel, VirtualNetworkModel, VlanIdPool)
from tests.utils import create_dpg_mock, create_vmware_vm_mock


class TestVirtualMachineModel(TestCase):
Expand Down Expand Up @@ -179,12 +91,11 @@ def test_to_vnc(self):
[vmi_model.vcenter_port.mac_address])
self.assertEqual(vnc_vmi.get_id_perms(), ID_PERMS)

@patch('cvm.models.find_vm_mac_address')
@patch('cvm.models.VirtualMachineInterfaceModel.vnc_vmi')
@patch('cvm.models.VirtualMachineInterfaceModel._should_construct_instance_ip')
def test_construct_instance_ip(self, should_construct, vnc_vmi_mock, _):
def test_construct_instance_ip(self, should_construct, vnc_vmi):
should_construct.return_value = True
vnc_vmi_mock.uuid = 'd376b6b4-943d-4599-862f-d852fd6ba425'
vnc_vmi.uuid = 'd376b6b4-943d-4599-862f-d852fd6ba425'

vmi_model = VirtualMachineInterfaceModel(self.vm_model, self.vn_model, self.vcenter_port)
vmi_model.construct_instance_ip()
Expand Down

0 comments on commit 9f11bf0

Please sign in to comment.