Add networks property to OS::Trove::Instance
Trove client recently added possibility to provide NICS while creating Trove instances, which is required in presence of multiple Neutron networks. This patch adds such property to OS::Trove::Instance resource, allowing usage of this new feature in Heat. Closes-Bug: #1312474 Change-Id: Ie691702bb2e2537397ed7ae657eb8d088424d890
This commit is contained in:
parent
04de60093b
commit
cab25ee8e0
@ -18,6 +18,7 @@ from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
from heat.openstack.common.gettextutils import _
|
||||
from heat.openstack.common import log as logging
|
||||
from heat.openstack.common import uuidutils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -37,10 +38,10 @@ class OSDBInstance(resource.Resource):
|
||||
|
||||
PROPERTIES = (
|
||||
NAME, FLAVOR, SIZE, DATABASES, USERS, AVAILABILITY_ZONE,
|
||||
RESTORE_POINT, DATASTORE_TYPE, DATASTORE_VERSION,
|
||||
RESTORE_POINT, DATASTORE_TYPE, DATASTORE_VERSION, NICS,
|
||||
) = (
|
||||
'name', 'flavor', 'size', 'databases', 'users', 'availability_zone',
|
||||
'restore_point', 'datastore_type', 'datastore_version',
|
||||
'restore_point', 'datastore_type', 'datastore_version', 'networks',
|
||||
)
|
||||
|
||||
_DATABASE_KEYS = (
|
||||
@ -55,6 +56,12 @@ class OSDBInstance(resource.Resource):
|
||||
'name', 'password', 'host', 'databases',
|
||||
)
|
||||
|
||||
_NICS_KEYS = (
|
||||
NET, PORT, V4_FIXED_IP
|
||||
) = (
|
||||
'network', 'port', 'fixed_ip'
|
||||
)
|
||||
|
||||
ATTRIBUTES = (
|
||||
HOSTNAME, HREF,
|
||||
) = (
|
||||
@ -98,6 +105,33 @@ class OSDBInstance(resource.Resource):
|
||||
constraints.Range(1, 150),
|
||||
]
|
||||
),
|
||||
NICS: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_("List of network interfaces to create on instance."),
|
||||
default=[],
|
||||
schema=properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
schema={
|
||||
NET: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name or UUID of the network to attach this NIC to. '
|
||||
'Either %(port)s or %(net)s must be specified.') % {
|
||||
'port': PORT, 'net': NET}
|
||||
),
|
||||
PORT: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name or UUID of Neutron port to attach this '
|
||||
'NIC to. '
|
||||
'Either %(port)s or %(net)s must be specified.') % {
|
||||
'port': PORT, 'net': NET}
|
||||
),
|
||||
V4_FIXED_IP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fixed IPv4 address for this NIC.')
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
DATABASES: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_('List of databases to be created on DB instance creation.'),
|
||||
@ -194,10 +228,10 @@ class OSDBInstance(resource.Resource):
|
||||
|
||||
attributes_schema = {
|
||||
HOSTNAME: attributes.Schema(
|
||||
_("Hostname of the instance")
|
||||
_("Hostname of the instance.")
|
||||
),
|
||||
HREF: attributes.Schema(
|
||||
_("Api endpoint reference of the instance")
|
||||
_("Api endpoint reference of the instance.")
|
||||
),
|
||||
}
|
||||
|
||||
@ -245,6 +279,30 @@ class OSDBInstance(resource.Resource):
|
||||
dbs = [{'name': db} for db in user.get(self.USER_DATABASES, [])]
|
||||
user[self.USER_DATABASES] = dbs
|
||||
|
||||
# convert networks to format required by troveclient
|
||||
nics = []
|
||||
for nic in self.properties.get(self.NICS):
|
||||
nic_dict = {}
|
||||
net = nic.get(self.NET)
|
||||
if net:
|
||||
if uuidutils.is_uuid_like(net):
|
||||
net_id = net
|
||||
else:
|
||||
# using Nova for lookup to cover both neutron and
|
||||
# nova-network cases
|
||||
nova = self.client('nova')
|
||||
net_id = nova.networks.find(label=net).id
|
||||
nic_dict['net-id'] = net_id
|
||||
port = nic.get(self.PORT)
|
||||
if port:
|
||||
neutron = self.client_plugin('neutron')
|
||||
nic_dict['port-id'] = neutron.find_neutron_resource(
|
||||
self.properties, self.PORT, 'port')
|
||||
ip = nic.get(self.V4_FIXED_IP)
|
||||
if ip:
|
||||
nic_dict['v4-fixed-ip'] = ip
|
||||
nics.append(nic_dict)
|
||||
|
||||
# create db instance
|
||||
instance = self.trove().instances.create(
|
||||
self._dbinstance_name(),
|
||||
@ -255,7 +313,8 @@ class OSDBInstance(resource.Resource):
|
||||
restorePoint=restore_point,
|
||||
availability_zone=zone,
|
||||
datastore=self.datastore_type,
|
||||
datastore_version=self.datastore_version)
|
||||
datastore_version=self.datastore_version,
|
||||
nics=nics)
|
||||
self.resource_id_set(instance.id)
|
||||
|
||||
return instance
|
||||
@ -372,9 +431,7 @@ class OSDBInstance(resource.Resource):
|
||||
|
||||
# check validity of user and databases
|
||||
users = self.properties.get(self.USERS)
|
||||
if not users:
|
||||
return
|
||||
|
||||
if users:
|
||||
databases = self.properties.get(self.DATABASES)
|
||||
if not databases:
|
||||
msg = _('Databases property is required if users property '
|
||||
@ -392,6 +449,19 @@ class OSDBInstance(resource.Resource):
|
||||
% {'dbs': missing_db, 'name': self.name})
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
# check validity of NICS
|
||||
is_neutron = self.is_using_neutron()
|
||||
nics = self.properties.get(self.NICS)
|
||||
for nic in nics:
|
||||
if not is_neutron and nic.get(self.PORT):
|
||||
msg = _("Can not use %s property on Nova-network.") % self.PORT
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
if bool(nic.get(self.NET)) == bool(nic.get(self.PORT)):
|
||||
msg = _("Either %(net)s or %(port)s must be provided.") % {
|
||||
'net': self.NET, 'port': self.PORT}
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
def href(self):
|
||||
if not self._href and self.dbinstance:
|
||||
if not self.dbinstance.links:
|
||||
|
@ -18,6 +18,8 @@ import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine.clients.os import neutron
|
||||
from heat.engine.clients.os import nova
|
||||
from heat.engine.clients.os import trove
|
||||
from heat.engine import parser
|
||||
from heat.engine.resources import os_database
|
||||
@ -49,6 +51,21 @@ db_template = '''
|
||||
}
|
||||
'''
|
||||
|
||||
db_template_with_nics = '''
|
||||
heat_template_version: 2013-05-23
|
||||
description: MySQL instance running on openstack DBaaS cloud
|
||||
resources:
|
||||
MySqlCloudDB:
|
||||
type: OS::Trove::Instance
|
||||
properties:
|
||||
name: test
|
||||
flavor: 1GB
|
||||
size: 30
|
||||
networks:
|
||||
- port: someportname
|
||||
fixed_ip: 1.2.3.4
|
||||
'''
|
||||
|
||||
|
||||
class FakeDBInstance(object):
|
||||
def __init__(self):
|
||||
@ -81,7 +98,10 @@ class FakeVersion(object):
|
||||
class OSDBInstanceTest(HeatTestCase):
|
||||
def setUp(self):
|
||||
super(OSDBInstanceTest, self).setUp()
|
||||
self.stub_keystoneclient()
|
||||
self.fc = self.m.CreateMockAnything()
|
||||
self.nova = self.m.CreateMockAnything()
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
|
||||
def _setup_test_clouddbinstance(self, name, t):
|
||||
stack_name = '%s_stack' % name
|
||||
@ -97,13 +117,16 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
stack)
|
||||
return instance
|
||||
|
||||
def _stubout_create(self, instance, fake_dbinstance):
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
def _stubout_common_create(self):
|
||||
trove.TroveClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc, 'flavors')
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, 'get_flavor_id')
|
||||
trove.TroveClientPlugin.get_flavor_id('1GB').AndReturn(1)
|
||||
self.m.StubOutWithMock(self.fc, 'instances')
|
||||
self.m.StubOutWithMock(self.fc.instances, 'create')
|
||||
|
||||
def _stubout_create(self, instance, fake_dbinstance):
|
||||
self._stubout_common_create()
|
||||
users = [{"name": "testuser", "password": "pass", "host": "%",
|
||||
"databases": [{"name": "validdb"}]}]
|
||||
databases = [{"collate": "utf8_general_ci",
|
||||
@ -115,17 +138,20 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
restorePoint=None,
|
||||
availability_zone=None,
|
||||
datastore="SomeDStype",
|
||||
datastore_version="MariaDB-5.5"
|
||||
datastore_version="MariaDB-5.5",
|
||||
nics=[]
|
||||
).AndReturn(fake_dbinstance)
|
||||
self.m.ReplayAll()
|
||||
|
||||
def _stubout_validate(self, instance):
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
def _stubout_validate(self, instance, neutron=None):
|
||||
trove.TroveClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc, 'datastore_versions')
|
||||
self.m.StubOutWithMock(self.fc.datastore_versions, 'list')
|
||||
self.fc.datastore_versions.list(instance.properties['datastore_type']
|
||||
).AndReturn([FakeVersion()])
|
||||
if neutron is not None:
|
||||
self.m.StubOutWithMock(instance, 'is_using_neutron')
|
||||
instance.is_using_neutron().AndReturn(bool(neutron))
|
||||
self.m.ReplayAll()
|
||||
|
||||
def test_osdatabase_create(self):
|
||||
@ -143,7 +169,6 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
t['Resources']['MySqlCloudDB']['Properties']['restore_point'] = "1234"
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_create', t)
|
||||
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
trove.TroveClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc, 'flavors')
|
||||
self.m.StubOutWithMock(self.fc.flavors, "list")
|
||||
@ -162,7 +187,8 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
restorePoint={"backupRef": "1234"},
|
||||
availability_zone=None,
|
||||
datastore="SomeDStype",
|
||||
datastore_version="MariaDB-5.5"
|
||||
datastore_version="MariaDB-5.5",
|
||||
nics=[]
|
||||
).AndReturn(fake_dbinstance)
|
||||
self.m.ReplayAll()
|
||||
|
||||
@ -277,7 +303,6 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
t = template_format.parse(db_template)
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
instance.resource_id = 12345
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
trove.TroveClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc, 'instances')
|
||||
self.m.StubOutWithMock(self.fc.instances, 'get')
|
||||
@ -292,7 +317,6 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
t = template_format.parse(db_template)
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
instance.resource_id = 12345
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
trove.TroveClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc, 'instances')
|
||||
self.m.StubOutWithMock(self.fc.instances, 'get')
|
||||
@ -308,7 +332,6 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
t = template_format.parse(db_template)
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
instance.resource_id = 12345
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
trove.TroveClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc, 'instances')
|
||||
self.m.StubOutWithMock(self.fc.instances, 'get')
|
||||
@ -415,7 +438,6 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
'datastore_type'] = 'mysql'
|
||||
t['Resources']['MySqlCloudDB']['Properties'].pop('datastore_version')
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
|
||||
trove.TroveClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc, 'datastore_versions')
|
||||
self.m.StubOutWithMock(self.fc.datastore_versions, 'list')
|
||||
@ -432,3 +454,130 @@ class OSDBInstanceTest(HeatTestCase):
|
||||
"Allowed versions are MariaDB-5.5, MariaDB-5.0.")
|
||||
self.assertEqual(expected_msg, six.text_type(ex))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_osdatabase_prop_validation_net_with_port_fail(self):
|
||||
t = template_format.parse(db_template)
|
||||
t['Resources']['MySqlCloudDB']['Properties']['networks'] = [
|
||||
{
|
||||
"port": "someportuuid",
|
||||
"network": "somenetuuid"
|
||||
}]
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
self._stubout_validate(instance, neutron=True)
|
||||
|
||||
ex = self.assertRaises(
|
||||
exception.StackValidationFailed, instance.validate)
|
||||
self.assertEqual('Either network or port must be provided.',
|
||||
six.text_type(ex))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_osdatabase_prop_validation_no_net_no_port_fail(self):
|
||||
t = template_format.parse(db_template)
|
||||
t['Resources']['MySqlCloudDB']['Properties']['networks'] = [
|
||||
{
|
||||
"fixed_ip": "1.2.3.4"
|
||||
}]
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
self._stubout_validate(instance, neutron=True)
|
||||
|
||||
ex = self.assertRaises(
|
||||
exception.StackValidationFailed, instance.validate)
|
||||
self.assertEqual('Either network or port must be provided.',
|
||||
six.text_type(ex))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_osdatabase_prop_validation_nic_port_on_novanet_fails(self):
|
||||
t = template_format.parse(db_template)
|
||||
t['Resources']['MySqlCloudDB']['Properties']['networks'] = [
|
||||
{
|
||||
"port": "someportuuid",
|
||||
}]
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
self._stubout_validate(instance, neutron=False)
|
||||
|
||||
ex = self.assertRaises(
|
||||
exception.StackValidationFailed, instance.validate)
|
||||
self.assertEqual('Can not use port property on Nova-network.',
|
||||
six.text_type(ex))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_osdatabase_create_with_port(self):
|
||||
fake_dbinstance = FakeDBInstance()
|
||||
t = template_format.parse(db_template_with_nics)
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
self._stubout_common_create()
|
||||
self.m.StubOutWithMock(neutron.NeutronClientPlugin,
|
||||
'find_neutron_resource')
|
||||
neutron.NeutronClientPlugin.find_neutron_resource(
|
||||
instance.properties, 'port', 'port').AndReturn('someportid')
|
||||
|
||||
self.fc.instances.create('test', 1, volume={'size': 30},
|
||||
databases=[],
|
||||
users=[],
|
||||
restorePoint=None,
|
||||
availability_zone=None,
|
||||
datastore=None,
|
||||
datastore_version=None,
|
||||
nics=[{'port-id': 'someportid',
|
||||
'v4-fixed-ip': '1.2.3.4'}]
|
||||
).AndReturn(fake_dbinstance)
|
||||
self.m.ReplayAll()
|
||||
|
||||
scheduler.TaskRunner(instance.create)()
|
||||
self.assertEqual((instance.CREATE, instance.COMPLETE), instance.state)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_osdatabase_create_with_net_id(self):
|
||||
net_id = '034aa4d5-0f36-4127-8481-5caa5bfc9403'
|
||||
fake_dbinstance = FakeDBInstance()
|
||||
t = template_format.parse(db_template_with_nics)
|
||||
t['resources']['MySqlCloudDB']['properties']['networks'] = [
|
||||
{'network': net_id}]
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
self._stubout_common_create()
|
||||
self.fc.instances.create('test', 1, volume={'size': 30},
|
||||
databases=[],
|
||||
users=[],
|
||||
restorePoint=None,
|
||||
availability_zone=None,
|
||||
datastore=None,
|
||||
datastore_version=None,
|
||||
nics=[{'net-id': net_id}]
|
||||
).AndReturn(fake_dbinstance)
|
||||
self.m.ReplayAll()
|
||||
|
||||
scheduler.TaskRunner(instance.create)()
|
||||
self.assertEqual((instance.CREATE, instance.COMPLETE), instance.state)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_osdatabase_create_with_net_name(self):
|
||||
|
||||
class FakeNet(object):
|
||||
id = 'somenetid'
|
||||
|
||||
fake_dbinstance = FakeDBInstance()
|
||||
t = template_format.parse(db_template_with_nics)
|
||||
t['resources']['MySqlCloudDB']['properties']['networks'] = [
|
||||
{'network': 'somenetname'}]
|
||||
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
|
||||
self._stubout_common_create()
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().AndReturn(self.nova)
|
||||
self.m.StubOutWithMock(self.nova, 'networks')
|
||||
self.m.StubOutWithMock(self.nova.networks, 'find')
|
||||
self.nova.networks.find(label='somenetname').AndReturn(FakeNet())
|
||||
|
||||
self.fc.instances.create('test', 1, volume={'size': 30},
|
||||
databases=[],
|
||||
users=[],
|
||||
restorePoint=None,
|
||||
availability_zone=None,
|
||||
datastore=None,
|
||||
datastore_version=None,
|
||||
nics=[{'net-id': 'somenetid'}]
|
||||
).AndReturn(fake_dbinstance)
|
||||
self.m.ReplayAll()
|
||||
|
||||
scheduler.TaskRunner(instance.create)()
|
||||
self.assertEqual((instance.CREATE, instance.COMPLETE), instance.state)
|
||||
self.m.VerifyAll()
|
||||
|
Loading…
Reference in New Issue
Block a user