Skip to content

Commit

Permalink
Prevent CVM from deleting all VMs on startup
Browse files Browse the repository at this point in the history
Use vrouter_uuid variable in VM and VMI services
Move _can_delete_from_vnc up to parent Service class
Split sync_vms into two methods and change the order of execution

Change-Id: Iff4bba6f821f45e0b0a595e931926215d0346cc2
Depends-On: Ib4b8106828f4e254bf64129a2a7346c38a1d57ad
Closes-Bug: #1776850
  • Loading branch information
aszc-dev committed Jun 14, 2018
1 parent 7dad5b8 commit 1c02c21
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 67 deletions.
3 changes: 2 additions & 1 deletion cvm/__main__.py
Expand Up @@ -55,7 +55,8 @@ def main(args):
vcenter_api_client=vcenter_api_client,
vnc_api_client=vnc_api_client,
vrouter_api_client=VRouterAPIClient(),
database=database
database=database,
esxi_api_client=esxi_api_client
)
vm_renamed_handler = VmRenamedHandler(vm_service, vmi_service)
vm_reconfigured_handler = VmReconfiguredHandler(vm_service, vmi_service)
Expand Down
5 changes: 5 additions & 0 deletions cvm/clients.py
Expand Up @@ -12,6 +12,7 @@
VNC_VCENTER_DEFAULT_SG, VNC_VCENTER_DEFAULT_SG_FQN,
VNC_VCENTER_IPAM, VNC_VCENTER_IPAM_FQN,
VNC_VCENTER_PROJECT)
from cvm.models import find_vrouter_uuid

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -134,6 +135,10 @@ def read_vm_properties(self, vmware_vm):
prop_set = object_set[0].propSet
return {prop.name: prop.val for prop in prop_set}

def read_vrouter_uuid(self):
host = self._datacenter.hostFolder.childEntity[0].host[0]
return find_vrouter_uuid(host)


class VCenterAPIClient(VSphereAPIClient):
def __init__(self, vcenter_cfg):
Expand Down
3 changes: 2 additions & 1 deletion cvm/controllers.py
Expand Up @@ -17,8 +17,9 @@ def __init__(self, vm_service, vn_service, vmi_service, handlers):
def initialize_database(self):
logger.info('Initializing database...')
self._vn_service.sync_vns()
self._vm_service.sync_vms()
self._vm_service.get_vms_from_vmware()
self._vmi_service.sync_vmis()
self._vm_service.delete_unused_vms_in_vnc()

def handle_update(self, update_set):
logger.info('Handling ESXi update.')
Expand Down
62 changes: 26 additions & 36 deletions cvm/services.py
Expand Up @@ -10,18 +10,35 @@


class Service(object):
def __init__(self, vnc_api_client, database):
def __init__(self, vnc_api_client, database, esxi_api_client=None, vcenter_api_client=None):
self._vnc_api_client = vnc_api_client
self._database = database
self._esxi_api_client = esxi_api_client
self._vcenter_api_client = vcenter_api_client
self._project = self._vnc_api_client.read_or_create_project()
self._default_security_group = self._vnc_api_client.read_or_create_security_group()
self._ipam = self._vnc_api_client.read_or_create_ipam()
if self._esxi_api_client:
self._vrouter_uuid = esxi_api_client.read_vrouter_uuid()

def _can_delete_from_vnc(self, vnc_obj):
if vnc_obj.get_type() == 'virtual-machine':
existing_obj = self._vnc_api_client.read_vm(vnc_obj.uuid)
if vnc_obj.get_type() == 'virtual-machine-interface':
existing_obj = self._vnc_api_client.read_vmi(vnc_obj.uuid)
existing_obj_vrouter_uuid = next(pair.value
for pair in existing_obj.get_annotations().key_value_pair
if pair.key == 'vrouter-uuid')
if existing_obj_vrouter_uuid == self._vrouter_uuid:
return True
logger.error('%s %s is managed by vRouter %s and cannot be deleted from VNC.',
vnc_obj.get_type(), vnc_obj.name, existing_obj_vrouter_uuid)
return False


class VirtualMachineService(Service):
def __init__(self, esxi_api_client, vnc_api_client, database):
super(VirtualMachineService, self).__init__(vnc_api_client, database)
self._esxi_api_client = esxi_api_client
super(VirtualMachineService, self).__init__(vnc_api_client, database, esxi_api_client=esxi_api_client)

def update(self, vmware_vm):
vm_properties = self._esxi_api_client.read_vm_properties(vmware_vm)
Expand All @@ -46,16 +63,12 @@ def _add_property_filter_for_vm(self, vm_model, filters):
property_filter = self._esxi_api_client.add_filter(vm_model.vmware_vm, filters)
vm_model.property_filter = property_filter

def sync_vms(self):
self._get_vms_from_vmware()
self._delete_unused_vms_in_vnc()

def _get_vms_from_vmware(self):
def get_vms_from_vmware(self):
vmware_vms = self._esxi_api_client.get_all_vms()
for vmware_vm in vmware_vms:
self.update(vmware_vm)

def _delete_unused_vms_in_vnc(self):
def delete_unused_vms_in_vnc(self):
vnc_vms = self._vnc_api_client.get_all_vms()
for vnc_vm in vnc_vms:
vm_model = self._database.get_vm_model_by_uuid(vnc_vm.uuid)
Expand All @@ -75,18 +88,6 @@ def remove_vm(self, name):
vm_model.destroy_property_filter()
return vm_model

def _can_delete_from_vnc(self, vnc_vm):
existing_vm = self._vnc_api_client.read_vm(vnc_vm.uuid)
vrouter_uuid = next(pair.value
for pair in vnc_vm.get_annotations().key_value_pair
if pair.key == 'vrouter-uuid')
if any([pair.key == 'vrouter-uuid' and pair.value == vrouter_uuid
for pair in existing_vm.get_annotations().key_value_pair]):
return True
logger.error('Virtual Machine %s is managed by vRouter %s and cannot be deleted from VNC.',
vnc_vm.name, vrouter_uuid)
return False

def set_tools_running_status(self, vmware_vm, value):
vm_model = self._database.get_vm_model_by_uuid(vmware_vm.config.instanceUuid)
if not vm_model:
Expand Down Expand Up @@ -122,10 +123,11 @@ def sync_vns(self):


class VirtualMachineInterfaceService(Service):
def __init__(self, vcenter_api_client, vnc_api_client, vrouter_api_client, database):
super(VirtualMachineInterfaceService, self).__init__(vnc_api_client, database)
def __init__(self, vcenter_api_client, vnc_api_client, vrouter_api_client, database, esxi_api_client=None):
super(VirtualMachineInterfaceService, self).__init__(vnc_api_client, database,
esxi_api_client=esxi_api_client,
vcenter_api_client=vcenter_api_client)
self.vrouter_api_client = vrouter_api_client
self._vcenter_api_client = vcenter_api_client

def sync_vmis(self):
self._create_new_vmis()
Expand Down Expand Up @@ -228,18 +230,6 @@ def _restore_vlan_id(self, vmi_model):
self._vcenter_api_client.restore_vlan_id(vmi_model.vn_model.dvs_name, vmi_model.port_key)
vmi_model.clear_vlan_id()

def _can_delete_from_vnc(self, vnc_vmi):
existing_vmi = self._vnc_api_client.read_vmi(vnc_vmi.uuid)
vrouter_uuid = next(pair.value
for pair in vnc_vmi.get_annotations().key_value_pair
if pair.key == 'vrouter-uuid')
if any([pair.key == 'vrouter-uuid' and pair.value == vrouter_uuid
for pair in existing_vmi.get_annotations().key_value_pair]):
return True
logger.error('Virtual Machine Interface %s is managed by vRouter %s and cannot be deleted from VNC.',
vnc_vmi.display_name, vrouter_uuid)
return False

def _delete_vrouter_port(self, vmi_model):
if vmi_model.vrouter_port_added:
logger.info('Deleting vRouter port for %s...', vmi_model.display_name)
Expand Down
54 changes: 25 additions & 29 deletions tests/test_services.py
Expand Up @@ -82,7 +82,7 @@ def test_sync_vms(self):
self.esxi_api_client.get_all_vms.return_value = [self.vmware_vm]
self.vnc_client.get_all_vms.return_value = []

self.vm_service.sync_vms()
self.vm_service.get_vms_from_vmware()

self.database.save.assert_called_once()
self.vnc_client.update_or_create_vm.assert_called_once()
Expand All @@ -93,7 +93,7 @@ def test_sync_no_vms(self):
self.esxi_api_client.get_all_vms.return_value = []
self.vnc_client.get_all_vms.return_value = []

self.vm_service.sync_vms()
self.vm_service.get_vms_from_vmware()

self.database.save.assert_not_called()
self.vnc_client.update_vm.assert_not_called()
Expand All @@ -106,9 +106,8 @@ def test_delete_unused_vms(self):

with patch('cvm.services.VirtualMachineService._can_delete_from_vnc') as can_delete:
can_delete.return_value = True
self.vm_service.sync_vms()
self.vm_service.delete_unused_vms_in_vnc()

self.database.save.assert_not_called()
self.vnc_client.delete_vm.assert_called_once_with(vnc_vm)

def test_remove_vm(self):
Expand Down Expand Up @@ -486,51 +485,48 @@ def test_read_no_ipam(self):
class TestCanDeleteFromVnc(TestCase):
def setUp(self):
self.vnc_api_client = Mock()
self.vm_service = VirtualMachineService(None, self.vnc_api_client, None)
self.vmi_service = VirtualMachineInterfaceService(None, self.vnc_api_client, None, None)
esxi_api_client = Mock()
esxi_api_client.read_vrouter_uuid.return_value = 'vrouter_uuid_1'
self.vm_service = VirtualMachineService(esxi_api_client, self.vnc_api_client, None)
self.vmi_service = VirtualMachineInterfaceService(esxi_api_client, self.vnc_api_client,
None, None, esxi_api_client=esxi_api_client)

def test_vnc_vm_true(self):
vnc_vm = Mock()
vnc_vm.get_annotations.return_value = KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_1')])
vnc_vm = vnc_api.VirtualMachine('VM', vnc_api.Project())
vnc_vm.set_annotations(KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_1')]))
self.vnc_api_client.read_vm.return_value = vnc_vm

result = self.vm_service._can_delete_from_vnc(vnc_vm)

self.assertTrue(result)

def test_vnc_vm_false(self):
vnc_vm_1 = Mock()
vnc_vm_1.get_annotations.return_value = KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_1')])
vnc_vm_2 = Mock()
vnc_vm_2.get_annotations.return_value = KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_2')])
self.vnc_api_client.read_vm.return_value = vnc_vm_1
vnc_vm = vnc_api.VirtualMachine('VM', vnc_api.Project())
vnc_vm.set_annotations(KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_2')]))
self.vnc_api_client.read_vm.return_value = vnc_vm

result = self.vm_service._can_delete_from_vnc(vnc_vm_2)
result = self.vm_service._can_delete_from_vnc(vnc_vm)

self.assertFalse(result)

def test_vnc_vmi_true(self):
vnc_vmi = Mock()
vnc_vmi.get_annotations.return_value = KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_1')])
vnc_vmi = vnc_api.VirtualMachineInterface('VMI', vnc_api.Project())
vnc_vmi.set_annotations(KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_1')]))
self.vnc_api_client.read_vmi.return_value = vnc_vmi

result = self.vmi_service._can_delete_from_vnc(vnc_vmi)

self.assertTrue(result)

def test_vnc_vmi_false(self):
vnc_vmi_1 = Mock()
vnc_vmi_1.get_annotations.return_value = KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_1')])
vnc_vmi_2 = Mock()
vnc_vmi_2.get_annotations.return_value = KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_2')])
self.vnc_api_client.read_vmi.return_value = vnc_vmi_1

result = self.vmi_service._can_delete_from_vnc(vnc_vmi_2)
vnc_vmi = vnc_api.VirtualMachineInterface('VMI', vnc_api.Project())
vnc_vmi.set_annotations(KeyValuePairs(
[KeyValuePair('vrouter-uuid', 'vrouter_uuid_2')]))
self.vnc_api_client.read_vmi.return_value = vnc_vmi

result = self.vmi_service._can_delete_from_vnc(vnc_vmi)

self.assertFalse(result)

0 comments on commit 1c02c21

Please sign in to comment.