hypervisor-supplied-nics support in PowerVM
PowerVM does not allow MAC addresses to be directly set on LPARs. Instead, use the hypervisor-supplied-nics feature to generate MAC addresses we can ensure are provisioned correctly. blueprint powervm-compute-resize-migration Change-Id: Idad7f03d9fd61970912ec1a6978fc0af56309df4
This commit is contained in:
@@ -23,7 +23,9 @@ from nova import db
|
|||||||
from nova import test
|
from nova import test
|
||||||
|
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.network import model as network_model
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
from nova.tests import fake_network_cache_model
|
||||||
from nova.virt import images
|
from nova.virt import images
|
||||||
from nova.virt.powervm import blockdev as powervm_blockdev
|
from nova.virt.powervm import blockdev as powervm_blockdev
|
||||||
from nova.virt.powervm import common
|
from nova.virt.powervm import common
|
||||||
@@ -156,8 +158,11 @@ class PowerVMDriverTestCase(test.TestCase):
|
|||||||
self.stubs.Set(images, 'fetch_to_raw', fake_image_fetch_to_raw)
|
self.stubs.Set(images, 'fetch_to_raw', fake_image_fetch_to_raw)
|
||||||
image_meta = {}
|
image_meta = {}
|
||||||
image_meta['id'] = '666'
|
image_meta['id'] = '666'
|
||||||
|
fake_net_info = network_model.NetworkInfo([
|
||||||
|
fake_network_cache_model.new_vif()])
|
||||||
self.powervm_connection.spawn(context.get_admin_context(),
|
self.powervm_connection.spawn(context.get_admin_context(),
|
||||||
self.instance, image_meta, 's3cr3t', [])
|
self.instance, image_meta, [], 's3cr3t',
|
||||||
|
fake_net_info)
|
||||||
state = self.powervm_connection.get_info(self.instance)['state']
|
state = self.powervm_connection.get_info(self.instance)['state']
|
||||||
self.assertEqual(state, power_state.RUNNING)
|
self.assertEqual(state, power_state.RUNNING)
|
||||||
|
|
||||||
@@ -176,12 +181,13 @@ class PowerVMDriverTestCase(test.TestCase):
|
|||||||
self.stubs.Set(
|
self.stubs.Set(
|
||||||
self.powervm_connection._powervm, '_cleanup',
|
self.powervm_connection._powervm, '_cleanup',
|
||||||
lambda *x, **y: raise_(Exception('This should be logged.')))
|
lambda *x, **y: raise_(Exception('This should be logged.')))
|
||||||
|
fake_net_info = network_model.NetworkInfo([
|
||||||
|
fake_network_cache_model.new_vif()])
|
||||||
self.assertRaises(exception.PowerVMImageCreationFailed,
|
self.assertRaises(exception.PowerVMImageCreationFailed,
|
||||||
self.powervm_connection.spawn,
|
self.powervm_connection.spawn,
|
||||||
context.get_admin_context(),
|
context.get_admin_context(),
|
||||||
self.instance,
|
self.instance,
|
||||||
{'id': 'ANY_ID'}, 's3cr3t', [])
|
{'id': 'ANY_ID'}, [], 's3cr3t', fake_net_info)
|
||||||
|
|
||||||
def test_destroy(self):
|
def test_destroy(self):
|
||||||
self.powervm_connection.destroy(self.instance, None)
|
self.powervm_connection.destroy(self.instance, None)
|
||||||
|
|||||||
@@ -88,10 +88,13 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||||||
def plug_vifs(self, instance, network_info):
|
def plug_vifs(self, instance, network_info):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def macs_for_instance(self, instance):
|
||||||
|
return self._powervm.macs_for_instance(instance)
|
||||||
|
|
||||||
def spawn(self, context, instance, image_meta, injected_files,
|
def spawn(self, context, instance, image_meta, injected_files,
|
||||||
admin_password, network_info=None, block_device_info=None):
|
admin_password, network_info=None, block_device_info=None):
|
||||||
"""Create a new instance/VM/domain on powerVM."""
|
"""Create a new instance/VM/domain on powerVM."""
|
||||||
self._powervm.spawn(context, instance, image_meta['id'])
|
self._powervm.spawn(context, instance, image_meta['id'], network_info)
|
||||||
|
|
||||||
def destroy(self, instance, network_info, block_device_info=None,
|
def destroy(self, instance, network_info, block_device_info=None,
|
||||||
destroy_disks=True):
|
destroy_disks=True):
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import decimal
|
import decimal
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@@ -170,7 +171,7 @@ class PowerVMOperator(object):
|
|||||||
|
|
||||||
self._host_stats = data
|
self._host_stats = data
|
||||||
|
|
||||||
def spawn(self, context, instance, image_id):
|
def spawn(self, context, instance, image_id, network_info):
|
||||||
def _create_lpar_instance(instance):
|
def _create_lpar_instance(instance):
|
||||||
host_stats = self.get_host_stats(refresh=True)
|
host_stats = self.get_host_stats(refresh=True)
|
||||||
inst_name = instance['name']
|
inst_name = instance['name']
|
||||||
@@ -201,9 +202,21 @@ class PowerVMOperator(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Network
|
# Network
|
||||||
|
# To ensure the MAC address on the guest matches the
|
||||||
|
# generated value, pull the first 10 characters off the
|
||||||
|
# MAC address for the mac_base_value parameter and then
|
||||||
|
# get the integer value of the final 2 characters as the
|
||||||
|
# slot_id parameter
|
||||||
|
mac = network_info[0]['address']
|
||||||
|
mac_base_value = (mac[:-2]).replace(':', '')
|
||||||
eth_id = self._operator.get_virtual_eth_adapter_id()
|
eth_id = self._operator.get_virtual_eth_adapter_id()
|
||||||
|
slot_id = int(mac[-2:], 16)
|
||||||
|
virtual_eth_adapters = ('%(slot_id)s/0/%(eth_id)s//0/0' %
|
||||||
|
locals())
|
||||||
|
|
||||||
# LPAR configuration data
|
# LPAR configuration data
|
||||||
|
# max_virtual_slots is hardcoded to 64 since we generate a MAC
|
||||||
|
# address that must be placed in slots 32 - 64
|
||||||
lpar_inst = LPAR.LPAR(
|
lpar_inst = LPAR.LPAR(
|
||||||
name=inst_name, lpar_env='aixlinux',
|
name=inst_name, lpar_env='aixlinux',
|
||||||
min_mem=mem_min, desired_mem=mem,
|
min_mem=mem_min, desired_mem=mem,
|
||||||
@@ -213,10 +226,14 @@ class PowerVMOperator(object):
|
|||||||
min_proc_units=cpus_units_min,
|
min_proc_units=cpus_units_min,
|
||||||
desired_proc_units=cpus_units,
|
desired_proc_units=cpus_units,
|
||||||
max_proc_units=cpus_max,
|
max_proc_units=cpus_max,
|
||||||
virtual_eth_adapters='4/0/%s//0/0' % eth_id)
|
virtual_eth_mac_base_value=mac_base_value,
|
||||||
|
max_virtual_slots=64,
|
||||||
|
virtual_eth_adapters=virtual_eth_adapters)
|
||||||
|
|
||||||
LOG.debug(_("Creating LPAR instance '%s'") % instance['name'])
|
LOG.debug(_("Creating LPAR instance '%s'") % instance['name'])
|
||||||
self._operator.create_lpar(lpar_inst)
|
self._operator.create_lpar(lpar_inst)
|
||||||
|
#TODO(mjfork) capture the error and handle the error when the MAC
|
||||||
|
# prefix already exists on the system (1 in 2^28)
|
||||||
except nova_exception.ProcessExecutionError:
|
except nova_exception.ProcessExecutionError:
|
||||||
LOG.exception(_("LPAR instance '%s' creation failed") %
|
LOG.exception(_("LPAR instance '%s' creation failed") %
|
||||||
instance['name'])
|
instance['name'])
|
||||||
@@ -345,6 +362,9 @@ class PowerVMOperator(object):
|
|||||||
def power_on(self, instance_name):
|
def power_on(self, instance_name):
|
||||||
self._operator.start_lpar(instance_name)
|
self._operator.start_lpar(instance_name)
|
||||||
|
|
||||||
|
def macs_for_instance(self, instance):
|
||||||
|
return self._operator.macs_for_instance(instance)
|
||||||
|
|
||||||
|
|
||||||
class BaseOperator(object):
|
class BaseOperator(object):
|
||||||
"""Base operator for IVM and HMC managed systems."""
|
"""Base operator for IVM and HMC managed systems."""
|
||||||
@@ -573,6 +593,9 @@ class BaseOperator(object):
|
|||||||
self._connection, command, check_exit_code=check_exit_code)
|
self._connection, command, check_exit_code=check_exit_code)
|
||||||
return stdout.read().splitlines()
|
return stdout.read().splitlines()
|
||||||
|
|
||||||
|
def macs_for_instance(self, instance):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IVMOperator(BaseOperator):
|
class IVMOperator(BaseOperator):
|
||||||
"""Integrated Virtualization Manager (IVM) Operator.
|
"""Integrated Virtualization Manager (IVM) Operator.
|
||||||
@@ -583,3 +606,32 @@ class IVMOperator(BaseOperator):
|
|||||||
def __init__(self, ivm_connection):
|
def __init__(self, ivm_connection):
|
||||||
self.command = command.IVMCommand()
|
self.command = command.IVMCommand()
|
||||||
BaseOperator.__init__(self, ivm_connection)
|
BaseOperator.__init__(self, ivm_connection)
|
||||||
|
|
||||||
|
def macs_for_instance(self, instance):
|
||||||
|
"""Generates set of valid MAC addresses for an IVM instance."""
|
||||||
|
# NOTE(vish): We would prefer to use 0xfe here to ensure that linux
|
||||||
|
# bridge mac addresses don't change, but it appears to
|
||||||
|
# conflict with libvirt, so we use the next highest octet
|
||||||
|
# that has the unicast and locally administered bits set
|
||||||
|
# properly: 0xfa.
|
||||||
|
# Discussion: https://bugs.launchpad.net/nova/+bug/921838
|
||||||
|
# NOTE(mjfork): For IVM-based PowerVM, we cannot directly set a MAC
|
||||||
|
# address on an LPAR, but rather need to construct one
|
||||||
|
# that can be used. Retain the 0xfe as noted above,
|
||||||
|
# but ensure the final 3 hex values represent a value
|
||||||
|
# between 32 and 64 so we can assign as the slot id on
|
||||||
|
# the system.
|
||||||
|
# FA:xx:xx:xx:x0:[32-64]
|
||||||
|
|
||||||
|
macs = set()
|
||||||
|
mac_base = [0xfa,
|
||||||
|
random.randint(0x00, 0xff),
|
||||||
|
random.randint(0x00, 0xff),
|
||||||
|
random.randint(0x00, 0xff),
|
||||||
|
random.randint(0x00, 0xff) & 0xf0,
|
||||||
|
random.randint(0x00, 0x00)]
|
||||||
|
for n in range(32, 64):
|
||||||
|
mac_base[5] = n
|
||||||
|
macs.add(':'.join(map(lambda x: "%02x" % x, mac_base)))
|
||||||
|
|
||||||
|
return macs
|
||||||
|
|||||||
Reference in New Issue
Block a user