diff --git a/neutron-powervc/powervc/neutron/api/powervc_rpc.py b/neutron-powervc/powervc/neutron/api/powervc_rpc.py index a3ea1b0..473513b 100644 --- a/neutron-powervc/powervc/neutron/api/powervc_rpc.py +++ b/neutron-powervc/powervc/neutron/api/powervc_rpc.py @@ -8,6 +8,8 @@ from powervc.common.gettextutils import _ from powervc.neutron.common import utils from powervc.neutron.db import powervc_db_v2 +import time + LOG = logging.getLogger(__name__) @@ -105,3 +107,21 @@ class PVCRpcCallbacks(object): pvc_ins_uuid) LOG.info(_("- local_ids: %s"), local_ids) return local_ids + + def set_pvc_id_to_port(self, context, local_port_id, pvc_port_id): + LOG.info(_("Neutron Agent RPC: start set pvc id to port:")) + # Sometimes for db session data delay, repeat 3 times to get the + # latest port info from local neutron db. + local_port = None + fetchTimes = 0 + while True: + local_port = self.db.get_port(local_id=local_port_id) + # Delay 3 times, each 10 sec to fetch the local port db obj + if local_port or fetchTimes >= 2: + break + fetchTimes += 1 + LOG.info(_("Cannot get port from local temporarily, wait 10sec..")) + time.sleep(10) + + self.db.set_port_pvc_id(local_port, pvc_port_id) + LOG.info(_("End of set powervc uuid to port.")) diff --git a/nova-powervc/powervc/nova/driver/virt/powervc/driver.py b/nova-powervc/powervc/nova/driver/virt/powervc/driver.py index 4d9e0f7..4303d64 100644 --- a/nova-powervc/powervc/nova/driver/virt/powervc/driver.py +++ b/nova-powervc/powervc/nova/driver/virt/powervc/driver.py @@ -442,13 +442,63 @@ class PowerVCDriver(driver.ComputeDriver): """List the volumes attached to the specified instance.""" return self._service.list_os_attachments(server_id) - def attach_interface(self, instance, image_meta, network_info): - """Attach an interface to the instance.""" - raise NotImplementedError() + def attach_interface(self, instance, image_meta, vif): + """Attach an interface to the instance. + """ + context = nova.context.get_admin_context() + try: + server_id = instance.get('uuid') + LOG.debug(_("Local Server uuid: %s") % server_id) - def detach_interface(self, instance, network_info): - """Detach an interface from the instance.""" - raise NotImplementedError() + port_id, network_id, ipAddress = self.\ + _get_port_network_ipaddress_from_vif(vif) + except Exception as e: + with excutils.save_and_reraise_exception(): + LOG.error(_("attach interface failed with wrong paras: %s"), + e, instance=instance) + + # call service to attach interface + self._service.attach_interface(context, + instance, + port_id, + network_id, + ipAddress) + + def _get_port_network_ipaddress_from_vif(self, vif): + """Get port uuid, network uuid, and ip Address from vif + """ + local_port_id = '' + local_network_id = '' + ipAddress = '' + + local_port_id = vif.get('id') + local_network = vif.get('network') + if local_network: + local_network_id = local_network.get('id') + local_subnet = local_network.get('subnets') + if local_subnet and local_subnet[0]: + ipAddresses = local_subnet[0].get('ips') + if ipAddresses and ipAddresses[0]: + ipAddress = ipAddresses[0].get('address') + + LOG.debug(_("Local port uuid: %s") % local_port_id) + LOG.debug(_("Local network uuid: %s") % local_network_id) + LOG.debug(_("ip address: %s") % ipAddress) + return (local_port_id, local_network_id, ipAddress) + + def detach_interface(self, instance, vif): + """Detach an interface from the instance. + """ + context = nova.context.get_admin_context() + local_port_id = vif.get('id') + LOG.debug(_("Local port uuid: %s") % local_port_id) + if not local_port_id: + LOG.error(_("no port id found to detach the interface.")) + return + # call service to detach interface + self._service.detach_interface(context, + instance, + local_port_id) def migrate_disk_and_power_off(self, context, instance, dest, instance_type, network_info, diff --git a/nova-powervc/powervc/nova/driver/virt/powervc/rpcapi.py b/nova-powervc/powervc/nova/driver/virt/powervc/rpcapi.py index 840aa69..5764383 100644 --- a/nova-powervc/powervc/nova/driver/virt/powervc/rpcapi.py +++ b/nova-powervc/powervc/nova/driver/virt/powervc/rpcapi.py @@ -69,3 +69,13 @@ class NetworkAPI(object): method_name = "set_device_id_on_port_by_pvc_instance_uuid" result = self.rpcclient.call(ctxt, method_name, **kwargs) return result + + def set_pvc_id_to_port(self, ctxt, local_port_id, pvc_port_id): + """Set the pvc_port_id to local db port by local_port_id + """ + kwargs = {} + kwargs['local_port_id'] = local_port_id + kwargs['pvc_port_id'] = pvc_port_id + method_name = "set_pvc_id_to_port" + result = self.rpcclient.call(ctxt, method_name, **kwargs) + return result diff --git a/nova-powervc/powervc/nova/driver/virt/powervc/service.py b/nova-powervc/powervc/nova/driver/virt/powervc/service.py index 6e24327..f319706 100644 --- a/nova-powervc/powervc/nova/driver/virt/powervc/service.py +++ b/nova-powervc/powervc/nova/driver/virt/powervc/service.py @@ -1316,3 +1316,65 @@ class PowerVCService(object): if pvc_volume_id is not None and local_volume_id != '': cache_volume[pvc_volume_id] = local_volume_id return cache_volume + + def attach_interface(self, context, instance, local_port_id, + local_network_id, ipAddress): + """attach a new port to a specified vm + :param context: context for this action + :param instance: the vm instance that new interface attach to + :param local_port_id: the local port uuid + :param local_network_id: the powervc network uuid + :param ipAddress: the ipv4 address that set to the vm + """ + pvc_port_id = self.get_pvc_port_uuid(context, local_port_id) + # get client server instance from a db instance + server_with_pvc_id = self._get_server(instance) + # get the powervc client server instance from novaclient + server_client_obj = self._manager.get(server_with_pvc_id) + # PowerVC restAPI will thrown BadRequest exception if set port_id + # and net-id/ipaddress in the same time. + # So if there is powervc port id matches to local port id existed, set + # the net_id and ipaddress to ''. + # If there is no port in powervc matches to local port existed, get + # and call restAPI with net-id and ipAddress. + if pvc_port_id: + pvc_network_id = '' + ipAddress = '' + else: + # the 'net-id' will be changed to the 'uuid' in the boot method + pvc_network_id = self.get_pvc_network_uuid(context, + local_network_id) + LOG.debug(_("PowerVC nic uuid: %s") % pvc_network_id) + # get the raw_response data from patched novaclient interface attach + # function. For detail, see the extensions/nova.py#interface_attach() + server_client_obj.interface_attach(pvc_port_id, pvc_network_id, + ipAddress) + # Call Neutron RPC to update the pvc id to port obj immediately. + # self.set_pvc_id_to_port(context, local_port_id, pvc_port_id) + + # TODO Loop to get the pvc_id from local db. Return this method until + # pvc_id got verified in local db, Default timeout is 150s + + def detach_interface(self, context, instance, local_port_id): + """detach a port from a specified vm + :param context: context for this action + :param instance: the vm instance that new interface attach to + :param local_port_id: the local port uuid + """ + pvc_port_uuid = self._api.get_pvc_port_uuid(context, local_port_id) + LOG.debug(_("pvc_port_uuid to be detach: %s"), pvc_port_uuid) + # get client server instance from a db instance + server_with_pvc_id = self._get_server(instance) + # get the powervc client server instance from novaclient + server_client_obj = self._manager.get(server_with_pvc_id) + response = server_client_obj.interface_detach(pvc_port_uuid) + LOG.debug(_("detach response: %s"), response) + return response + + def set_pvc_id_to_port(self, ctx, local_port_id, pvc_port_id): + """ + After attach an interface to a server, update the neutorn ports + to reflect latest ports information to neutron db. + """ + pvc_id = self._api.set_pvc_id_to_port(ctx, local_port_id, pvc_port_id) + return pvc_id