Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Move port managing to a separate service
Add read_port to VRouterAPIClient
Add _port_needs_an_update method in VMI Service
Add VRouterPortService to manage ports on vRouter

Partial-Bug: #1777404
Change-Id: I5515d46bb12e575f024184252ada244759264331
  • Loading branch information
aszc-dev committed Jun 19, 2018
1 parent 443750e commit 1cf1aa6
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 54 deletions.
16 changes: 10 additions & 6 deletions cvm/__main__.py
Expand Up @@ -14,7 +14,8 @@
from cvm.database import Database
from cvm.monitors import VMwareMonitor
from cvm.services import (VirtualMachineInterfaceService,
VirtualMachineService, VirtualNetworkService)
VirtualMachineService, VirtualNetworkService,
VRouterPortService)


def load_config(config_file):
Expand Down Expand Up @@ -54,15 +55,18 @@ def main(args):
vmi_service = VirtualMachineInterfaceService(
vcenter_api_client=vcenter_api_client,
vnc_api_client=vnc_api_client,
vrouter_api_client=VRouterAPIClient(),
database=database,
esxi_api_client=esxi_api_client
)
vm_renamed_handler = VmRenamedHandler(vm_service, vmi_service)
vm_reconfigured_handler = VmReconfiguredHandler(vm_service, vmi_service)
vm_removed_handler = VmRemovedHandler(vm_service, vmi_service)
vrouter_port_service = VRouterPortService(
vrouter_api_client=VRouterAPIClient(),
database=database
)
vm_renamed_handler = VmRenamedHandler(vm_service, vmi_service, vrouter_port_service)
vm_reconfigured_handler = VmReconfiguredHandler(vm_service, vmi_service, vrouter_port_service)
vm_removed_handler = VmRemovedHandler(vm_service, vmi_service, vrouter_port_service)
handlers = [vm_renamed_handler, vm_reconfigured_handler, vm_removed_handler]
vmware_controller = VmwareController(vm_service, vn_service, vmi_service, handlers)
vmware_controller = VmwareController(vm_service, vn_service, vmi_service, vrouter_port_service, handlers)
vmware_monitor = VMwareMonitor(esxi_api_client, vmware_controller)

greenlets = [
Expand Down
14 changes: 14 additions & 0 deletions cvm/clients.py
@@ -1,7 +1,9 @@
import atexit
import json
import logging
from uuid import uuid4

import requests
from contrail_vrouter_api.vrouter_api import ContrailVRouterApi
from pyVim.connect import Disconnect, SmartConnectNoSSL
from pyVmomi import vim, vmodl # pylint: disable=no-name-in-module
Expand Down Expand Up @@ -425,6 +427,8 @@ class VRouterAPIClient(object):

def __init__(self):
self.vrouter_api = ContrailVRouterApi()
self.vrouter_host = 'http://localhost/'
self.vrouter_port = '9091'

def add_port(self, vmi_model):
""" Add port to VRouter Agent. """
Expand Down Expand Up @@ -457,3 +461,13 @@ def enable_port(self, vmi_uuid):
self.vrouter_api.enable_port(vmi_uuid)
except Exception, e:
logger.error('There was a problem with vRouter API Client: %s', e)

def read_port(self, vmi_uuid):
request_url = '{host}:{port}/port/{uuid}'.format(host=self.vrouter_host,
port=self.vrouter_port,
uuid=vmi_uuid)
response = requests.get(request_url)
if response.status_code != requests.codes.ok:
return None

return json.loads(response.content)
17 changes: 13 additions & 4 deletions cvm/controllers.py
Expand Up @@ -8,10 +8,11 @@


class VmwareController(object):
def __init__(self, vm_service, vn_service, vmi_service, handlers):
def __init__(self, vm_service, vn_service, vmi_service, vrouter_port_service, handlers):
self._vm_service = vm_service
self._vn_service = vn_service
self._vmi_service = vmi_service
self._vrouter_port_service = vrouter_port_service
self._handlers = handlers

def initialize_database(self):
Expand All @@ -20,6 +21,7 @@ def initialize_database(self):
self._vm_service.get_vms_from_vmware()
self._vmi_service.sync_vmis()
self._vm_service.delete_unused_vms_in_vnc()
self._vrouter_port_service.sync_ports()

def handle_update(self, update_set):
logger.info('Handling ESXi update.')
Expand Down Expand Up @@ -69,6 +71,7 @@ def _handle_vm_updated_event(self, event):
try:
vm_model = self._vm_service.update(vmware_vm)
self._vmi_service.update_vmis_for_vm_model(vm_model)
self._vrouter_port_service.sync_ports()
except vmodl.fault.ManagedObjectNotFound:
logger.info('Skipping event for a non-existent VM.')

Expand Down Expand Up @@ -110,23 +113,26 @@ def _handle_event(self, event):
class VmRenamedHandler(AbstractEventHandler):
EVENTS = (vim.event.VmRenamedEvent,)

def __init__(self, vm_service, vmi_service):
def __init__(self, vm_service, vmi_service, vrouter_port_service):
self._vm_service = vm_service
self._vmi_service = vmi_service
self._vrouter_port_service = vrouter_port_service

def _handle_event(self, event):
old_name = event.oldName
new_name = event.newName
self._vm_service.rename_vm(old_name, new_name)
self._vmi_service.rename_vmis(new_name)
self._vrouter_port_service.sync_ports()


class VmReconfiguredHandler(AbstractEventHandler):
EVENTS = (vim.event.VmReconfiguredEvent,)

def __init__(self, vm_service, vmi_service):
def __init__(self, vm_service, vmi_service, vrouter_port_service):
self._vm_service = vm_service
self._vmi_service = vmi_service
self._vrouter_port_service = vrouter_port_service

def _handle_event(self, event):
logger.info('Detected VmReconfiguredEvent')
Expand All @@ -139,18 +145,21 @@ def _handle_event(self, event):
portgroup_key = device.backing.port.portgroupKey
self._vm_service.update_vm_models_interface(vmware_vm, mac_address, portgroup_key)
self._vmi_service.update_vmis_vn(vmware_vm, mac_address, portgroup_key)
self._vrouter_port_service.sync_ports()
else:
logger.info('Detected VmReconfiguredEvent with unsupported %s device', type(device))


class VmRemovedHandler(AbstractEventHandler):
EVENTS = (vim.event.VmRemovedEvent,)

def __init__(self, vm_service, vmi_service):
def __init__(self, vm_service, vmi_service, vrouter_port_service):
self._vm_service = vm_service
self._vmi_service = vmi_service
self._vrouter_port_service = vrouter_port_service

def _handle_event(self, event):
vm_name = event.vm.name
self._vmi_service.remove_vmis_for_vm_model(vm_name)
self._vm_service.remove_vm(vm_name)
self._vrouter_port_service.sync_ports()
2 changes: 2 additions & 0 deletions cvm/database.py
Expand Up @@ -13,6 +13,8 @@ def __init__(self):
self.vm_models = {}
self.vn_models = {}
self.vmi_models = {}
self.ports_to_update = []
self.ports_to_delete = []

def save(self, obj):
if isinstance(obj, VirtualMachineModel):
Expand Down
51 changes: 37 additions & 14 deletions cvm/services.py
Expand Up @@ -123,11 +123,10 @@ def sync_vns(self):


class VirtualMachineInterfaceService(Service):
def __init__(self, vcenter_api_client, vnc_api_client, vrouter_api_client, database, esxi_api_client=None):
def __init__(self, vcenter_api_client, vnc_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

def sync_vmis(self):
self._create_new_vmis()
Expand Down Expand Up @@ -192,14 +191,7 @@ def _delete_unused_vmis(self):
self._vnc_api_client.delete_vmi(vnc_vmi.get_uuid())

def _add_or_update_vrouter_port(self, vmi_model):
if vmi_model.vrouter_port_added:
logger.info('vRouter port for %s already exists. Updating...', vmi_model.mac_address)
self.vrouter_api_client.delete_port(vmi_model.uuid)
else:
logger.info('Adding new vRouter port for %s...', vmi_model.mac_address)
self.vrouter_api_client.add_port(vmi_model)
self.vrouter_api_client.enable_port(vmi_model.uuid)
vmi_model.vrouter_port_added = True
self._database.ports_to_update.append(vmi_model)

def update_nic(self, nic_info):
vmi_model = self._database.get_vmi_model_by_uuid(VirtualMachineInterfaceModel.get_uuid(nic_info.macAddress))
Expand Down Expand Up @@ -229,10 +221,7 @@ def _restore_vlan_id(self, vmi_model):
vmi_model.clear_vlan_id()

def _delete_vrouter_port(self, vmi_model):
if vmi_model.vrouter_port_added:
logger.info('Deleting vRouter port for %s...', vmi_model.display_name)
self.vrouter_api_client.delete_port(vmi_model.uuid)
vmi_model.vrouter_port_added = False
self._database.ports_to_delete.append(vmi_model.uuid)

def remove_vmis_for_vm_model(self, vm_name):
vm_model = self._database.get_vm_model_by_name(vm_name)
Expand Down Expand Up @@ -261,3 +250,37 @@ def _get_vn_from_vmi(vnc_vmi):
@staticmethod
def _get_vm_from_vmi(vnc_vmi):
return vnc_vmi.get_virtual_machine_refs()[0]


class VRouterPortService(object):
def __init__(self, vrouter_api_client, database):
self._vrouter_api_client = vrouter_api_client
self._database = database

def sync_ports(self):
for uuid in self._database.ports_to_delete:
self._delete_port(uuid)

for vmi_model in self._database.ports_to_update:
if self._port_needs_an_update(vmi_model):
self._update_port(vmi_model)

def _port_needs_an_update(self, vmi_model):
vrouter_port = self._vrouter_api_client.read_port(vmi_model.uuid)
if not vrouter_port:
return True
return (vrouter_port.get('instance-id') != vmi_model.vm_model.uuid or
vrouter_port.get('vn-id') != vmi_model.vn_model.uuid or
vrouter_port.get('rx-vlan-id') != vmi_model.vlan_id or
vrouter_port.get('tx-vlan-id') != vmi_model.vlan_id or
vrouter_port.get('ip-address') != vmi_model.vnc_instance_ip.instance_ip_address)

def _update_port(self, vmi_model):
self._vrouter_api_client.delete_port(vmi_model.uuid)
self._vrouter_api_client.add_port(vmi_model)
self._vrouter_api_client.enable_port(vmi_model.uuid)
self._database.ports_to_update.remove(vmi_model)

def _delete_port(self, uuid):
self._vrouter_api_client.delete_port(uuid)
self._database.ports_to_delete.remove(uuid)
7 changes: 4 additions & 3 deletions tests/test_controllers.py
Expand Up @@ -24,11 +24,12 @@ def setUp(self):
self.database = Mock()
self.vm_service = Mock(database=self.database)
self.vmi_service = Mock()
self.vrouter_port_service = Mock()

vm_renamed_handler = VmRenamedHandler(self.vm_service, self.vmi_service)
vm_removed_handler = VmRemovedHandler(self.vm_service, self.vmi_service)
vm_renamed_handler = VmRenamedHandler(self.vm_service, self.vmi_service, self.vrouter_port_service)
vm_removed_handler = VmRemovedHandler(self.vm_service, self.vmi_service, Mock())
handlers = [vm_renamed_handler, vm_removed_handler]
self.vmware_controller = VmwareController(self.vm_service, None, self.vmi_service,
self.vmware_controller = VmwareController(self.vm_service, None, self.vmi_service, None,
handlers)

@patch.object(VmwareController, '_handle_change')
Expand Down
22 changes: 12 additions & 10 deletions tests/test_events.py
Expand Up @@ -11,7 +11,7 @@
from cvm.database import Database
from cvm.models import VirtualNetworkModel, VlanIdPool
from cvm.services import (VirtualMachineInterfaceService,
VirtualMachineService, VirtualNetworkService)
VirtualMachineService, VirtualNetworkService, VRouterPortService)


def create_ipam():
Expand Down Expand Up @@ -213,8 +213,9 @@ def test_vm_created(vcenter_api_client, vn_model_1, vm_created_update,
vrouter_api_client = Mock()
database = Database()
vm_service = VirtualMachineService(esxi_api_client, vnc_api_client, database)
vmi_service = VirtualMachineInterfaceService(vcenter_api_client, vnc_api_client, vrouter_api_client, database)
controller = VmwareController(vm_service, None, vmi_service, [])
vmi_service = VirtualMachineInterfaceService(vcenter_api_client, vnc_api_client, database)
vrouter_port_service = VRouterPortService(vrouter_api_client, database)
controller = VmwareController(vm_service, None, vmi_service, vrouter_port_service, [])

# Virtual Networks are already created for us and after synchronization,
# their models are stored in our database
Expand Down Expand Up @@ -277,11 +278,11 @@ def test_vm_renamed(vcenter_api_client, vn_model_1, vm_created_update,
vmi_service = VirtualMachineInterfaceService(
vcenter_api_client,
vnc_api_client,
vrouter_api_client,
database
)
vm_renamed_handler = VmRenamedHandler(vm_service, vmi_service)
controller = VmwareController(vm_service, None, vmi_service, [vm_renamed_handler])
vrouter_port_service = VRouterPortService(vrouter_api_client, database)
vm_renamed_handler = VmRenamedHandler(vm_service, vmi_service, vrouter_port_service)
controller = VmwareController(vm_service, None, vmi_service, vrouter_port_service, [vm_renamed_handler])

# Virtual Networks are already created for us and after synchronization,
# their models are stored in our database
Expand Down Expand Up @@ -349,11 +350,11 @@ def test_vm_reconfigured(vcenter_api_client, vn_model_1, vn_model_2, vm_created_
vmi_service = VirtualMachineInterfaceService(
vcenter_api_client,
vnc_api_client,
vrouter_api_client,
database
)
vm_reconfigure_handler = VmReconfiguredHandler(vm_service, vmi_service)
controller = VmwareController(vm_service, vn_service, vmi_service, [vm_reconfigure_handler])
vrouter_port_service = VRouterPortService(vrouter_api_client, database)
vm_reconfigure_handler = VmReconfiguredHandler(vm_service, vmi_service, vrouter_port_service)
controller = VmwareController(vm_service, vn_service, vmi_service, vrouter_port_service, [vm_reconfigure_handler])

# Virtual Networks are already created for us and after synchronization,
# their models are stored in our database
Expand Down Expand Up @@ -402,7 +403,8 @@ def test_vm_reconfigured(vcenter_api_client, vn_model_1, vn_model_2, vm_created_
assert vnc_vn_2.uuid in [ref['uuid'] for ref in new_instance_ip.get_virtual_network_refs()]

# Check if VMI's vRouter Port has been updated:
vrouter_api_client.delete_port.assert_called_once_with(vmi_model.uuid)
assert vrouter_api_client.delete_port.call_count == 3
assert vrouter_api_client.delete_port.call_args[0][0] == vmi_model.uuid
assert vrouter_api_client.add_port.call_count == 2
assert vrouter_api_client.add_port.call_args[0][0] == vmi_model

Expand Down

0 comments on commit 1cf1aa6

Please sign in to comment.