Skip to content

Commit

Permalink
Add functional test for VM power state change
Browse files Browse the repository at this point in the history
Add functional test for VM vmware tools running change
Move fixtures and util functions to separate files
Add controller fixture for functional tests
Split functional tests to separate files

Change-Id: I87cf8f4ab6d6b584b406f6240e51896324f18174
Partial-Bug: #1776434
  • Loading branch information
krzysztofg256 authored and aszc-dev committed Jul 13, 2018
1 parent 6e43f20 commit f861cb8
Show file tree
Hide file tree
Showing 21 changed files with 716 additions and 646 deletions.
16 changes: 8 additions & 8 deletions cvm/models.py
Expand Up @@ -45,17 +45,17 @@ def update_interfaces(self, vmware_vm):
self.ports = self._read_ports()
self.vmi_models = self._construct_interfaces()

def is_tools_running_status_changed(self, tools_running_status):
return tools_running_status != self.vm_properties['guest.toolsRunningStatus']

def update_tools_running_status(self, tools_running_status):
if tools_running_status != self.vm_properties['guest.toolsRunningStatus']:
self.vm_properties['guest.toolsRunningStatus'] = tools_running_status
return True
return False
self.vm_properties['guest.toolsRunningStatus'] = tools_running_status

def is_power_state_changed(self, power_state):
return power_state != self.vm_properties['runtime.powerState']

def update_power_state(self, power_state):
if power_state != self.vm_properties['runtime.powerState']:
self.vm_properties['runtime.powerState'] = power_state
return True
return False
self.vm_properties['runtime.powerState'] = power_state

def _read_ports(self):
try:
Expand Down
6 changes: 4 additions & 2 deletions cvm/services.py
Expand Up @@ -120,7 +120,8 @@ def update_vmware_tools_status(self, vmware_vm, tools_running_status):
vm_model = self._database.get_vm_model_by_uuid(vmware_vm.config.instanceUuid)
if not vm_model:
return
if vm_model.update_tools_running_status(tools_running_status):
if vm_model.is_tools_running_status_changed(tools_running_status):
vm_model.update_tools_running_status(tools_running_status)
logger.info('VMware tools on VM %s are %s', vm_model.name,
'running' if vm_model.tools_running else 'not running')
self._database.save(vm_model)
Expand All @@ -147,7 +148,8 @@ def update_vm_models_interfaces(self, vmware_vm):

def update_power_state(self, vmware_vm, power_state):
vm_model = self._database.get_vm_model_by_uuid(vmware_vm.config.instanceUuid)
if vm_model.update_power_state(power_state):
if vm_model.is_power_state_changed(power_state):
vm_model.update_power_state(power_state)
logger.info('VM %s was powered %s', vm_model.name, power_state[7:].lower())
for vmi_model in vm_model.vmi_models:
self._database.ports_to_update.append(vmi_model)
Expand Down
Empty file added tests/functional/__init__.py
Empty file.
233 changes: 233 additions & 0 deletions tests/functional/conftest.py
@@ -0,0 +1,233 @@
# pylint: disable=redefined-outer-name
import pytest
from mock import Mock
from pyVmomi import vim, vmodl # pylint: disable=no-name-in-module
from vnc_api import vnc_api

from cvm.controllers import (GuestNetHandler, UpdateHandler,
VmReconfiguredHandler, VmRenamedHandler,
VmUpdatedHandler, VmwareController, PowerStateHandler, VmwareToolsStatusHandler)
from cvm.database import Database
from cvm.models import VlanIdPool
from cvm.services import (VirtualMachineInterfaceService,
VirtualMachineService, VirtualNetworkService,
VRouterPortService)
from tests.utils import (assign_ip_to_instance_ip, create_vn_model,
create_vnc_vn, wrap_into_update_set)


@pytest.fixture()
def vnc_vn_1():
return create_vnc_vn(name='DPG1', uuid='vnc_vn_uuid_1')


@pytest.fixture()
def vnc_vn_2():
return create_vnc_vn(name='DPG2', uuid='vnc_vn_uuid_2')


@pytest.fixture()
def vn_model_1(vnc_vn_1):
return create_vn_model(vnc_vn=vnc_vn_1, portgroup_key='dvportgroup-1')


@pytest.fixture()
def vn_model_2(vnc_vn_2):
return create_vn_model(vnc_vn=vnc_vn_2, portgroup_key='dvportgroup-2')


@pytest.fixture()
def vmware_vm_1():
vmware_vm = Mock(spec=vim.VirtualMachine)
vmware_vm.summary.runtime.host.vm = []
vmware_vm.config.instanceUuid = '12345678-1234-1234-1234-123456789012'
backing = Mock(spec=vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo)
backing.port = Mock(portgroupKey='dvportgroup-1', portKey='10')
vmware_vm.config.hardware.device = [Mock(backing=backing, macAddress='11:11:11:11:11:11')]
return vmware_vm


@pytest.fixture()
def vm_properties_1():
return {
'config.instanceUuid': '12345678-1234-1234-1234-123456789012',
'name': 'VM1',
'runtime.powerState': 'poweredOn',
'guest.toolsRunningStatus': 'guestToolsRunning',
}


@pytest.fixture()
def vm_properties_renamed():
return {
'config.instanceUuid': '12345678-1234-1234-1234-123456789012',
'name': 'VM1-renamed',
'runtime.powerState': 'poweredOn',
'guest.toolsRunningStatus': 'guestToolsRunning',
}


@pytest.fixture()
def contrail_vm_properties():
return {
'config.instanceUuid': '12345678-1234-1234-1234-123456789012',
'name': 'ContrailVM',
'runtime.powerState': 'poweredOn',
'guest.toolsRunningStatus': 'guestToolsRunning',
}


@pytest.fixture()
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=event)


@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=event)


@pytest.fixture()
def vm_reconfigured_update(vmware_vm_1):
event = Mock(spec=vim.event.VmReconfiguredEvent())
event.vm.vm = vmware_vm_1
port = Mock(spec=vim.dvs.PortConnection())
port.portgroupKey = 'dvportgroup-2'
device = Mock(spec=vim.vm.device.VirtualVmxnet3())
device.backing.port = port
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=event)


@pytest.fixture()
def vnc_api_client():
vnc_client = Mock()
project = vnc_api.Project()
project.set_uuid('project-uuid')
vnc_client.read_or_create_project.return_value = project
vnc_client.create_and_read_instance_ip.side_effect = assign_ip_to_instance_ip
return vnc_client

@pytest.fixture()
def vcenter_api_client():
vcenter_client = Mock()
vcenter_client.__enter__ = Mock()
vcenter_client.__exit__ = Mock()
vcenter_client.get_ip_pool_for_dpg.return_value = None
return vcenter_client


@pytest.fixture()
def esxi_api_client(vm_properties_1):
esxi_client = Mock()
esxi_client.read_vm_properties.return_value = vm_properties_1
return esxi_client


@pytest.fixture()
def lock():
semaphore = Mock()
semaphore.__enter__ = Mock()
semaphore.__exit__ = Mock()
return semaphore


@pytest.fixture()
def vlan_id_pool():
vlan_pool = VlanIdPool(0, 100)
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=change)


@pytest.fixture()
def vm_power_off_state_update(vmware_vm_1):
change = Mock(spec=vmodl.query.PropertyCollector.Change())
change.name = 'runtime.powerState'
change.val = 'poweredOff'
return wrap_into_update_set(change=change, obj=vmware_vm_1)


@pytest.fixture()
def vm_power_on_state_update(vmware_vm_1):
change = Mock(spec=vmodl.query.PropertyCollector.Change())
change.name = 'runtime.powerState'
change.val = 'poweredOn'
return wrap_into_update_set(change=change, obj=vmware_vm_1)


@pytest.fixture()
def vm_disable_running_tools_update(vmware_vm_1):
change = Mock(spec=vmodl.query.PropertyCollector.Change())
change.name = 'guest.toolsRunningStatus'
change.val = 'guestToolsNotRunning'
return wrap_into_update_set(change=change, obj=vmware_vm_1)


@pytest.fixture()
def vm_enable_running_tools_update(vmware_vm_1):
change = Mock(spec=vmodl.query.PropertyCollector.Change())
change.name = 'guest.toolsRunningStatus'
change.val = 'guestToolsRunning'
return wrap_into_update_set(change=change, obj=vmware_vm_1)


@pytest.fixture()
def vm_service(esxi_api_client, vnc_api_client, database):
return VirtualMachineService(esxi_api_client, vnc_api_client, database)


@pytest.fixture()
def vn_service(esxi_api_client, vnc_api_client, database):
return VirtualNetworkService(esxi_api_client, vnc_api_client, database)


@pytest.fixture()
def vmi_service(vcenter_api_client, vnc_api_client, database, vlan_id_pool):
return VirtualMachineInterfaceService(vcenter_api_client, vnc_api_client,
database, vlan_id_pool=vlan_id_pool)


@pytest.fixture()
def vrouter_port_service(vrouter_api_client, database):
return VRouterPortService(vrouter_api_client, database)


@pytest.fixture()
def database():
return Database()


@pytest.fixture()
def vrouter_api_client():
return Mock()


@pytest.fixture()
def controller(vm_service, vn_service, vmi_service, vrouter_port_service, lock):
handlers = [
VmUpdatedHandler(vm_service, vn_service, vmi_service, vrouter_port_service),
VmRenamedHandler(vm_service, vmi_service, vrouter_port_service),
VmReconfiguredHandler(vm_service, vn_service, vmi_service, vrouter_port_service),
GuestNetHandler(vmi_service, vrouter_port_service),
PowerStateHandler(vm_service, vrouter_port_service),
VmwareToolsStatusHandler(vm_service)
]
update_handler = UpdateHandler(handlers)
return VmwareController(vm_service, vn_service, vmi_service, vrouter_port_service, update_handler, lock)
16 changes: 16 additions & 0 deletions tests/functional/test_contrail_vm.py
@@ -0,0 +1,16 @@
def test_contrail_vm(controller, database, esxi_api_client, vnc_api_client, vrouter_api_client, vm_created_update,
contrail_vm_properties):
""" We don't need ContrailVM model for CVM to operate properly. """
esxi_api_client.read_vm_properties.return_value = contrail_vm_properties
# A new update containing VmCreatedEvent arrives and is being handled by the controller
controller.handle_update(vm_created_update)

# VM model has not been saved in the database
assert not database.get_all_vm_models()

# There were no calls to vnc_api
vnc_api_client.update_or_create_vm.assert_not_called()
vnc_api_client.update_vmi.assert_not_called()

# There were no calls to vrouter_api
vrouter_api_client.add_port.assert_not_called()
34 changes: 34 additions & 0 deletions tests/functional/test_external_ipam.py
@@ -0,0 +1,34 @@
from tests.utils import dont_assign_ip_to_instance_ip


def test_external_ipam(controller, database, vnc_api_client, vrouter_api_client, vm_created_update, nic_info_update,
vn_model_1):
""" We don't need ContrailVM model for CVM to operate properly. """

# 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_vmi.assert_called_once()
60 changes: 60 additions & 0 deletions tests/functional/test_vm_created.py
@@ -0,0 +1,60 @@
from tests.utils import (assert_vm_model_state, assert_vmi_model_state,
assert_vnc_vm_state, assert_vnc_vmi_state,
reserve_vlan_ids)


def test_vm_created(controller, database, vcenter_api_client, vnc_api_client, vrouter_api_client, vlan_id_pool,
vm_created_update, vnc_vn_1, vn_model_1):
# Virtual Networks are already created for us and after synchronization,
# their models are stored in our database
database.save(vn_model_1)

# Some vlan ids should be already reserved
vcenter_api_client.get_vlan_id.return_value = None
reserve_vlan_ids(vlan_id_pool, [0, 1])

# A new update containing VmCreatedEvent arrives and is being handled by the controller
controller.handle_update(vm_created_update)

# Check if VM Model has been saved properly:
# - in VNC:
vnc_api_client.update_or_create_vm.assert_called_once()
vnc_vm = vnc_api_client.update_or_create_vm.call_args[0][0]
assert_vnc_vm_state(vnc_vm, uuid='12345678-1234-1234-1234-123456789012',
name='12345678-1234-1234-1234-123456789012', owner='project-uuid')

# - in Database:
vm_model = database.get_vm_model_by_uuid('12345678-1234-1234-1234-123456789012')
assert_vm_model_state(vm_model, uuid='12345678-1234-1234-1234-123456789012', name='VM1')

# Check if VMI Model has been saved properly:
# - in VNC
vnc_api_client.update_vmi.assert_called_once()
vnc_vmi = vnc_api_client.update_vmi.call_args[0][0]
assert_vnc_vmi_state(vnc_vmi, mac_address='11:11:11:11:11:11',
vnc_vm_uuid=vnc_vm.uuid, vnc_vn_uuid=vnc_vn_1.uuid)

# - in Database
vmi_model = database.get_all_vmi_models()[0]

# Check if VMI Model's Instance IP has been created in VNC:
vnc_api_client.create_and_read_instance_ip.assert_called_once()

# Check if VMI's vRouter Port has been added:
vrouter_api_client.add_port.assert_called_once_with(vmi_model)

# Check if VLAN ID has been set using VLAN Override
vcenter_port = vcenter_api_client.set_vlan_id.call_args[0][0]
assert vcenter_port.port_key == '10'
assert vcenter_port.vlan_id == 2

# Check inner VMI model state
assert_vmi_model_state(
vmi_model,
mac_address='11:11:11:11:11:11',
ip_address='192.168.100.5',
vlan_id=2,
display_name='vmi-DPG1-VM1',
vn_model=vn_model_1,
vm_model=vm_model
)

0 comments on commit f861cb8

Please sign in to comment.