Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add functional test for VM power state change
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
1 parent
6e43f20
commit f861cb8
Showing
21 changed files
with
716 additions
and
646 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
) |
Oops, something went wrong.