Add/Remove/Modify vNIC post deployment Edit

User call the following hosting cli to add a vNIC with IP and port
specified:
    nova interface-attach --net-id {net_uuid} --fixed-ip {ipAddress}
<server_name>
PowerVC driver handle the request in the following driver method:
    def attach_interface(self, instance, image_meta, network_info):
then call the PowerVC restAPI to attach the interface to server.

User call the following hosting cli to remove a vNIC with port
specified:
    nova interface-detach <server-id> <port-id>
PowerVC driver handle the request in the following driver method:
    def detach_interface(self, instance, network_info):
then call the PowerVC restAPI to attach the interface to server.

Change-Id: If0ae14a86005da06dc35620c2af47f37112228d5
Closes-Bug: #1375132
This commit is contained in:
Jerry Cai 2014-09-29 14:52:06 +08:00
parent 3b9f42bf53
commit fd05a48a19
4 changed files with 148 additions and 6 deletions

View File

@ -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."))

View File

@ -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,

View File

@ -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

View File

@ -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