Add Neutron support
Currently create instance doesnt work in OS installation with Neutron. To get it work, additional parameter 'nics' should be specified in Nova 'create' call. This change allows user to pass 'nics' parameter when create instance. Also possible to specify list of network IDs which should be attached to instance if networks are not specified by user. Closes-Bug: #1257838 Change-Id: Ifd98a880dfa22e9677e802bcd779acd9146ec671
This commit is contained in:
parent
dc0504de51
commit
bd54d99aa0
@ -85,6 +85,12 @@ volume = {
|
||||
}
|
||||
}
|
||||
|
||||
nics = {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
}
|
||||
}
|
||||
|
||||
databases_ref_list = {
|
||||
"type": "array",
|
||||
@ -199,7 +205,8 @@ instance = {
|
||||
"type": non_empty_string,
|
||||
"version": non_empty_string
|
||||
}
|
||||
}
|
||||
},
|
||||
"nics": nics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,6 +238,11 @@ common_opts = [
|
||||
default=['atom', 'json', 'xml'],
|
||||
help='Filetype endings not to be reattached to an id '
|
||||
'by the utils method correct_id_with_req.'),
|
||||
cfg.ListOpt('default_neutron_networks',
|
||||
default=[],
|
||||
help='List of network IDs which should be attached'
|
||||
' to instance when networks are not specified'
|
||||
' in API call.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -451,7 +451,7 @@ class Instance(BuiltInstance):
|
||||
@classmethod
|
||||
def create(cls, context, name, flavor_id, image_id, databases, users,
|
||||
datastore, datastore_version, volume_size, backup_id,
|
||||
availability_zone=None):
|
||||
availability_zone=None, nics=None):
|
||||
|
||||
client = create_nova_client(context)
|
||||
try:
|
||||
@ -481,6 +481,11 @@ class Instance(BuiltInstance):
|
||||
raise exception.BackupFileNotFound(
|
||||
location=backup_info.location)
|
||||
|
||||
if not nics and CONF.default_neutron_networks:
|
||||
nics = []
|
||||
for net_id in CONF.default_neutron_networks:
|
||||
nics.append({"net-id": net_id})
|
||||
|
||||
def _create_resources():
|
||||
|
||||
db_info = DBInstance.create(name=name, flavor_id=flavor_id,
|
||||
@ -513,7 +518,7 @@ class Instance(BuiltInstance):
|
||||
datastore_version.packages,
|
||||
volume_size, backup_id,
|
||||
availability_zone,
|
||||
root_password)
|
||||
root_password, nics)
|
||||
|
||||
return SimpleInstance(context, db_info, service_status,
|
||||
root_password)
|
||||
|
@ -215,11 +215,16 @@ class InstanceController(wsgi.Controller):
|
||||
else:
|
||||
availability_zone = None
|
||||
|
||||
if 'nics' in body['instance']:
|
||||
nics = body['instance']['nics']
|
||||
else:
|
||||
nics = None
|
||||
|
||||
instance = models.Instance.create(context, name, flavor_id,
|
||||
image_id, databases, users,
|
||||
datastore, datastore_version,
|
||||
volume_size, backup_id,
|
||||
availability_zone)
|
||||
availability_zone, nics)
|
||||
|
||||
view = views.InstanceDetailView(instance, req=req)
|
||||
return wsgi.Result(view.data(), 200)
|
||||
|
@ -102,7 +102,7 @@ class API(proxy.RpcProxy):
|
||||
def create_instance(self, instance_id, name, flavor,
|
||||
image_id, databases, users, datastore_manager,
|
||||
packages, volume_size, backup_id=None,
|
||||
availability_zone=None, root_password=None):
|
||||
availability_zone=None, root_password=None, nics=None):
|
||||
LOG.debug("Making async call to create instance %s " % instance_id)
|
||||
self.cast(self.context,
|
||||
self.make_msg("create_instance",
|
||||
@ -117,4 +117,4 @@ class API(proxy.RpcProxy):
|
||||
volume_size=volume_size,
|
||||
backup_id=backup_id,
|
||||
availability_zone=availability_zone,
|
||||
root_password=root_password))
|
||||
root_password=root_password, nics=nics))
|
||||
|
@ -83,12 +83,12 @@ class Manager(periodic_task.PeriodicTasks):
|
||||
def create_instance(self, context, instance_id, name, flavor,
|
||||
image_id, databases, users, datastore_manager,
|
||||
packages, volume_size, backup_id, availability_zone,
|
||||
root_password):
|
||||
root_password, nics):
|
||||
instance_tasks = FreshInstanceTasks.load(context, instance_id)
|
||||
instance_tasks.create_instance(flavor, image_id, databases, users,
|
||||
datastore_manager, packages,
|
||||
volume_size, backup_id,
|
||||
availability_zone, root_password)
|
||||
availability_zone, root_password, nics)
|
||||
|
||||
if CONF.exists_notification_transformer:
|
||||
@periodic_task.periodic_task(
|
||||
|
@ -147,7 +147,7 @@ class ConfigurationMixin(object):
|
||||
class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
def create_instance(self, flavor, image_id, databases, users,
|
||||
datastore_manager, packages, volume_size,
|
||||
backup_id, availability_zone, root_password):
|
||||
backup_id, availability_zone, root_password, nics):
|
||||
|
||||
LOG.debug(_("begin create_instance for id: %s") % self.id)
|
||||
security_groups = None
|
||||
@ -175,7 +175,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
image_id,
|
||||
datastore_manager,
|
||||
volume_size,
|
||||
availability_zone)
|
||||
availability_zone,
|
||||
nics)
|
||||
elif use_nova_server_volume:
|
||||
volume_info = self._create_server_volume(
|
||||
flavor['id'],
|
||||
@ -183,7 +184,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
security_groups,
|
||||
datastore_manager,
|
||||
volume_size,
|
||||
availability_zone)
|
||||
availability_zone,
|
||||
nics)
|
||||
else:
|
||||
volume_info = self._create_server_volume_individually(
|
||||
flavor['id'],
|
||||
@ -191,7 +193,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
security_groups,
|
||||
datastore_manager,
|
||||
volume_size,
|
||||
availability_zone)
|
||||
availability_zone,
|
||||
nics)
|
||||
|
||||
config = self._render_config(datastore_manager, flavor, self.id)
|
||||
|
||||
@ -302,7 +305,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
|
||||
def _create_server_volume(self, flavor_id, image_id, security_groups,
|
||||
datastore_manager, volume_size,
|
||||
availability_zone):
|
||||
availability_zone, nics):
|
||||
LOG.debug(_("begin _create_server_volume for id: %s") % self.id)
|
||||
server = None
|
||||
try:
|
||||
@ -321,7 +324,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
name, image_id, flavor_id,
|
||||
files=files, volume=volume_ref,
|
||||
security_groups=security_groups,
|
||||
availability_zone=availability_zone)
|
||||
availability_zone=availability_zone, nics=nics)
|
||||
LOG.debug(_("Created new compute instance %(server_id)s "
|
||||
"for id: %(id)s") %
|
||||
{'server_id': server.id, 'id': self.id})
|
||||
@ -349,14 +352,16 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
|
||||
def _create_server_volume_heat(self, flavor, image_id,
|
||||
datastore_manager,
|
||||
volume_size, availability_zone):
|
||||
volume_size, availability_zone, nics):
|
||||
LOG.debug(_("begin _create_server_volume_heat for id: %s") % self.id)
|
||||
try:
|
||||
client = create_heat_client(self.context)
|
||||
|
||||
ifaces, ports = self._build_heat_nics(nics)
|
||||
template_obj = template.load_heat_template(datastore_manager)
|
||||
heat_template_unicode = template_obj.render(
|
||||
volume_support=CONF.trove_volume_support)
|
||||
volume_support=CONF.trove_volume_support,
|
||||
ifaces=ifaces, ports=ports)
|
||||
try:
|
||||
heat_template = heat_template_unicode.encode('utf-8')
|
||||
except UnicodeEncodeError:
|
||||
@ -422,7 +427,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
def _create_server_volume_individually(self, flavor_id, image_id,
|
||||
security_groups, datastore_manager,
|
||||
volume_size,
|
||||
availability_zone):
|
||||
availability_zone, nics):
|
||||
LOG.debug(_("begin _create_server_volume_individually for id: %s") %
|
||||
self.id)
|
||||
server = None
|
||||
@ -432,7 +437,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
server = self._create_server(flavor_id, image_id, security_groups,
|
||||
datastore_manager,
|
||||
block_device_mapping,
|
||||
availability_zone)
|
||||
availability_zone, nics)
|
||||
server_id = server.id
|
||||
# Save server ID.
|
||||
self.update_db(compute_instance_id=server_id)
|
||||
@ -522,7 +527,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
|
||||
def _create_server(self, flavor_id, image_id, security_groups,
|
||||
datastore_manager, block_device_mapping,
|
||||
availability_zone):
|
||||
availability_zone, nics):
|
||||
files = {"/etc/guest_info": ("[DEFAULT]\nguest_id=%s\n"
|
||||
"datastore_manager=%s\n"
|
||||
"tenant_id=%s\n" %
|
||||
@ -542,7 +547,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
server = self.nova_client.servers.create(
|
||||
name, image_id, flavor_id, files=files, userdata=userdata,
|
||||
security_groups=security_groups, block_device_mapping=bdmap,
|
||||
availability_zone=availability_zone)
|
||||
availability_zone=availability_zone, nics=nics)
|
||||
LOG.debug(_("Created new compute instance %(server_id)s "
|
||||
"for id: %(id)s") %
|
||||
{'server_id': server.id, 'id': self.id})
|
||||
@ -616,6 +621,27 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
)
|
||||
return [security_group["name"]]
|
||||
|
||||
def _build_heat_nics(self, nics):
|
||||
ifaces = []
|
||||
ports = []
|
||||
if nics:
|
||||
for idx, nic in enumerate(nics):
|
||||
iface_id = nic.get('port-id')
|
||||
if iface_id:
|
||||
ifaces.append(iface_id)
|
||||
continue
|
||||
net_id = nic.get('net-id')
|
||||
if net_id:
|
||||
port = {}
|
||||
port['name'] = "Port%s" % idx
|
||||
port['net_id'] = net_id
|
||||
fixed_ip = nic.get('v4-fixed-ip')
|
||||
if fixed_ip:
|
||||
port['fixed_ip'] = fixed_ip
|
||||
ports.append(port)
|
||||
ifaces.append("{Ref: Port%s}" % idx)
|
||||
return ifaces, ports
|
||||
|
||||
|
||||
class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
|
||||
"""
|
||||
|
@ -18,6 +18,16 @@ Parameters:
|
||||
TenantId:
|
||||
Type: String
|
||||
Resources:
|
||||
{% for port in ports %}
|
||||
{{ port.name }}:
|
||||
Type: OS::Neutron::Port
|
||||
Properties:
|
||||
network_id: "{{ port.net_id }}"
|
||||
security_groups: [{Ref: MySqlDbaasSG}]
|
||||
{% if port.fixed_ip %}
|
||||
fixed_ips: [{"ip_address": "{{ port.fixed_ip }}"}]
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
BaseInstance:
|
||||
Type: AWS::EC2::Instance
|
||||
Metadata:
|
||||
@ -38,7 +48,11 @@ Resources:
|
||||
ImageId: {Ref: ImageId}
|
||||
InstanceType: {Ref: Flavor}
|
||||
AvailabilityZone: {Ref: AvailabilityZone}
|
||||
SecurityGroups : [{Ref: MySqlDbaasSG}]
|
||||
{% if ifaces %}
|
||||
NetworkInterfaces: [{{ ifaces|join(', ') }}]
|
||||
{% else %}
|
||||
SecurityGroups: [{Ref: MySqlDbaasSG}]
|
||||
{% endif %}
|
||||
UserData:
|
||||
Fn::Base64:
|
||||
Fn::Join:
|
||||
|
@ -387,6 +387,15 @@ class CreateInstance(object):
|
||||
None, databases, availability_zone="NOOP")
|
||||
assert_equal(400, dbaas.last_http_code)
|
||||
|
||||
@test(enabled=not FAKE)
|
||||
def test_create_with_bad_nics(self):
|
||||
instance_name = "instance-failure-with-bad-nics"
|
||||
databases = []
|
||||
assert_raises(exceptions.BadRequest, dbaas.instances.create,
|
||||
instance_name, instance_info.dbaas_flavor_href,
|
||||
None, databases, nics="BAD")
|
||||
assert_equal(400, dbaas.last_http_code)
|
||||
|
||||
@test(enabled=VOLUME_SUPPORT)
|
||||
def test_create_failure_with_empty_volume(self):
|
||||
instance_name = "instance-failure-with-no-volume-size"
|
||||
|
@ -260,7 +260,7 @@ class FakeServers(object):
|
||||
|
||||
def create(self, name, image_id, flavor_ref, files=None, userdata=None,
|
||||
block_device_mapping=None, volume=None, security_groups=None,
|
||||
availability_zone=None):
|
||||
availability_zone=None, nics=None):
|
||||
id = "FAKE_%s" % uuid.uuid4()
|
||||
if volume:
|
||||
volume = self.volumes.create(volume['size'], volume['name'],
|
||||
|
@ -48,7 +48,8 @@ class fake_Server:
|
||||
|
||||
class fake_ServerManager:
|
||||
def create(self, name, image_id, flavor_id, files, userdata,
|
||||
security_groups, block_device_mapping, availability_zone=None):
|
||||
security_groups, block_device_mapping, availability_zone=None,
|
||||
nics=None):
|
||||
server = fake_Server()
|
||||
server.id = "server_id"
|
||||
server.name = name
|
||||
@ -59,6 +60,7 @@ class fake_ServerManager:
|
||||
server.security_groups = security_groups
|
||||
server.block_device_mapping = block_device_mapping
|
||||
server.availability_zone = availability_zone
|
||||
server.nics = nics
|
||||
return server
|
||||
|
||||
|
||||
@ -180,33 +182,33 @@ class FreshInstanceTasksTest(testtools.TestCase):
|
||||
when(taskmanager_models.CONF).get("cloudinit_location").thenReturn(
|
||||
cloudinit_location)
|
||||
server = self.freshinstancetasks._create_server(
|
||||
None, None, None, datastore_manager, None, None)
|
||||
None, None, None, datastore_manager, None, None, None)
|
||||
self.assertEqual(server.userdata, self.userdata)
|
||||
|
||||
def test_create_instance_guestconfig(self):
|
||||
when(taskmanager_models.CONF).get("guest_config").thenReturn(
|
||||
self.guestconfig)
|
||||
server = self.freshinstancetasks._create_server(
|
||||
None, None, None, "test", None, None)
|
||||
None, None, None, "test", None, None, None)
|
||||
self.assertTrue('/etc/trove-guestagent.conf' in server.files)
|
||||
self.assertEqual(server.files['/etc/trove-guestagent.conf'],
|
||||
self.guestconfig_content)
|
||||
|
||||
def test_create_instance_with_az_kwarg(self):
|
||||
server = self.freshinstancetasks._create_server(
|
||||
None, None, None, None, None, availability_zone='nova')
|
||||
None, None, None, None, None, availability_zone='nova', nics=None)
|
||||
|
||||
self.assertIsNotNone(server)
|
||||
|
||||
def test_create_instance_with_az(self):
|
||||
server = self.freshinstancetasks._create_server(
|
||||
None, None, None, None, None, 'nova')
|
||||
None, None, None, None, None, 'nova', None)
|
||||
|
||||
self.assertIsNotNone(server)
|
||||
|
||||
def test_create_instance_with_az_none(self):
|
||||
server = self.freshinstancetasks._create_server(
|
||||
None, None, None, None, None, None)
|
||||
None, None, None, None, None, None, None)
|
||||
|
||||
self.assertIsNotNone(server)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user