~ubuntu-branches/ubuntu/trusty/horizon/trusty-updates

« back to all changes in this revision

Viewing changes to openstack_dashboard/dashboards/project/instances/workflows/create_instance.py

  • Committer: Package Import Robot
  • Author(s): Adam Gandelman
  • Date: 2013-09-06 11:59:43 UTC
  • mfrom: (1.1.30)
  • Revision ID: package-import@ubuntu.com-20130906115943-h3td0l7tp16mb9oc
Tags: 1:2013.2~b3-0ubuntu1
* New upstream release.
* debian/control: Minimum python-openstack-auth version >= 1.1.1.
* debian/control: Add python-troveclient.
* debian/static: Refresh static assets for 2013.2~b3.
* debian/patches: ubuntu_local_settings.patch -> ubuntu_settings.patch, also
  patch location of secret key in openstack_dashboard/settings.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import json
22
22
import logging
23
23
 
24
 
from django.utils.text import normalize_newlines
25
 
from django.utils.translation import ugettext_lazy as _
26
 
from django.views.decorators.debug import sensitive_variables
 
24
from django.conf import settings  # noqa
 
25
from django.template.defaultfilters import filesizeformat  # noqa
 
26
from django.utils.text import normalize_newlines  # noqa
 
27
from django.utils.translation import ugettext_lazy as _  # noqa
 
28
from django.utils.translation import ungettext_lazy  # noqa
 
29
from django.views.decorators.debug import sensitive_variables  # noqa
27
30
 
28
31
from horizon import exceptions
29
32
from horizon import forms
 
33
from horizon.utils import fields
 
34
from horizon.utils import functions
30
35
from horizon.utils import validators
31
36
from horizon import workflows
32
37
 
33
38
from openstack_dashboard import api
34
39
from openstack_dashboard.api import cinder
 
40
from openstack_dashboard.usage import quotas
35
41
 
36
 
from openstack_dashboard.dashboards.project.images_and_snapshots.utils \
37
 
    import get_available_images
 
42
from openstack_dashboard.dashboards.project.images_and_snapshots import utils
38
43
 
39
44
 
40
45
LOG = logging.getLogger(__name__)
67
72
    contributes = ("project_id", "user_id")
68
73
 
69
74
 
70
 
class VolumeOptionsAction(workflows.Action):
71
 
    VOLUME_CHOICES = (
72
 
        ('', _("Don't boot from a volume.")),
 
75
class SetInstanceDetailsAction(workflows.Action):
 
76
    SOURCE_TYPE_CHOICES = (
 
77
        ('', _("--- Select source ---")),
 
78
        ("image_id", _("Boot from image.")),
 
79
        ("instance_snapshot_id", _("Boot from snapshot.")),
73
80
        ("volume_id", _("Boot from volume.")),
 
81
        ("volume_image_id", _("Boot from image "
 
82
                                  "(creates a new volume).")),
74
83
        ("volume_snapshot_id", _("Boot from volume snapshot "
75
84
                                 "(creates a new volume).")),
76
85
    )
77
 
    # Boot from volume options
78
 
    volume_type = forms.ChoiceField(label=_("Volume Options"),
79
 
                                    choices=VOLUME_CHOICES,
80
 
                                    required=False)
 
86
 
 
87
    availability_zone = forms.ChoiceField(label=_("Availability Zone"),
 
88
                                          required=False)
 
89
 
 
90
    name = forms.CharField(max_length=80, label=_("Instance Name"))
 
91
 
 
92
    flavor = forms.ChoiceField(label=_("Flavor"),
 
93
                               help_text=_("Size of image to launch."))
 
94
 
 
95
    count = forms.IntegerField(label=_("Instance Count"),
 
96
                               min_value=1,
 
97
                               initial=1,
 
98
                               help_text=_("Number of instances to launch."))
 
99
 
 
100
    source_type = forms.ChoiceField(label=_("Instance Boot Source"),
 
101
                                    required=True,
 
102
                                    choices=SOURCE_TYPE_CHOICES,
 
103
                                    help_text=_("Choose Your Boot Source "
 
104
                                                "Type."))
 
105
 
 
106
    instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
 
107
                                             required=False)
 
108
 
81
109
    volume_id = forms.ChoiceField(label=_("Volume"), required=False)
 
110
 
82
111
    volume_snapshot_id = forms.ChoiceField(label=_("Volume Snapshot"),
83
 
                                           required=False)
 
112
                                               required=False)
 
113
 
 
114
    image_id = forms.ChoiceField(
 
115
        label=_("Image Name"),
 
116
        required=False,
 
117
        widget=fields.SelectWidget(
 
118
            data_attrs=('volume_size',),
 
119
            transform=lambda x: ("%s (%s)" % (x.name,
 
120
                                              filesizeformat(x.bytes)))))
 
121
 
 
122
    volume_size = forms.CharField(label=_("Device size (GB)"),
 
123
                                  required=False,
 
124
                                  help_text=_("Volume size in gigabytes "
 
125
                                              "(integer value)."))
 
126
 
84
127
    device_name = forms.CharField(label=_("Device Name"),
85
128
                                  required=False,
86
129
                                  initial="vda",
87
130
                                  help_text=_("Volume mount point (e.g. 'vda' "
88
131
                                              "mounts at '/dev/vda')."))
 
132
 
89
133
    delete_on_terminate = forms.BooleanField(label=_("Delete on Terminate"),
90
134
                                             initial=False,
91
135
                                             required=False,
93
137
                                                         "instance terminate"))
94
138
 
95
139
    class Meta:
96
 
        name = _("Volume Options")
97
 
        permissions = ('openstack.services.volume',)
98
 
        help_text_template = ("project/instances/"
99
 
                              "_launch_volumes_help.html")
100
 
 
101
 
    def clean(self):
102
 
        cleaned_data = super(VolumeOptionsAction, self).clean()
103
 
        volume_opt = cleaned_data.get('volume_type', None)
104
 
 
105
 
        if volume_opt and not cleaned_data[volume_opt]:
106
 
            raise forms.ValidationError(_('Please choose a volume, or select '
107
 
                                          '%s.') % self.VOLUME_CHOICES[0][1])
108
 
        return cleaned_data
109
 
 
110
 
    def _get_volume_display_name(self, volume):
111
 
        if hasattr(volume, "volume_id"):
112
 
            vol_type = "snap"
113
 
            visible_label = _("Snapshot")
114
 
        else:
115
 
            vol_type = "vol"
116
 
            visible_label = _("Volume")
117
 
        return (("%s:%s" % (volume.id, vol_type)),
118
 
                (_("%(name)s - %(size)s GB (%(label)s)") %
119
 
                 {'name': volume.display_name or volume.id,
120
 
                  'size': volume.size,
121
 
                  'label': visible_label}))
122
 
 
123
 
    def populate_volume_id_choices(self, request, context):
124
 
        volume_options = [("", _("Select Volume"))]
125
 
        try:
126
 
            volumes = [v for v in cinder.volume_list(self.request)
127
 
                       if v.status == api.cinder.VOLUME_STATE_AVAILABLE]
128
 
            volume_options.extend([self._get_volume_display_name(vol)
129
 
                                   for vol in volumes])
130
 
        except:
131
 
            exceptions.handle(self.request,
132
 
                              _('Unable to retrieve list of volumes.'))
133
 
        return volume_options
134
 
 
135
 
    def populate_volume_snapshot_id_choices(self, request, context):
136
 
        volume_options = [("", _("Select Volume Snapshot"))]
137
 
        try:
138
 
            snapshots = cinder.volume_snapshot_list(self.request)
139
 
            snapshots = [s for s in snapshots
140
 
                         if s.status == api.cinder.VOLUME_STATE_AVAILABLE]
141
 
            volume_options.extend([self._get_volume_display_name(snap)
142
 
                                   for snap in snapshots])
143
 
        except:
144
 
            exceptions.handle(self.request,
145
 
                              _('Unable to retrieve list of volume '
146
 
                                'snapshots.'))
147
 
 
148
 
        return volume_options
149
 
 
150
 
 
151
 
class VolumeOptions(workflows.Step):
152
 
    action_class = VolumeOptionsAction
153
 
    depends_on = ("project_id", "user_id")
154
 
    contributes = ("volume_type",
155
 
                   "volume_id",
156
 
                   "device_name",  # Can be None for an image.
157
 
                   "delete_on_terminate")
158
 
 
159
 
    def contribute(self, data, context):
160
 
        context = super(VolumeOptions, self).contribute(data, context)
161
 
        # Translate form input to context for volume values.
162
 
        if "volume_type" in data and data["volume_type"]:
163
 
            context['volume_id'] = data.get(data['volume_type'], None)
164
 
 
165
 
        if not context.get("volume_type", ""):
166
 
            context['volume_type'] = self.action.VOLUME_CHOICES[0][0]
167
 
            context['volume_id'] = None
168
 
            context['device_name'] = None
169
 
            context['delete_on_terminate'] = None
170
 
        return context
171
 
 
172
 
 
173
 
class SetInstanceDetailsAction(workflows.Action):
174
 
    SOURCE_TYPE_CHOICES = (
175
 
        ("image_id", _("Image")),
176
 
        ("instance_snapshot_id", _("Snapshot")),
177
 
    )
178
 
    source_type = forms.ChoiceField(label=_("Instance Source"),
179
 
                                    choices=SOURCE_TYPE_CHOICES)
180
 
    image_id = forms.ChoiceField(label=_("Image"), required=False)
181
 
    instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
182
 
                                             required=False)
183
 
    availability_zone = forms.ChoiceField(label=_("Availability Zone"),
184
 
                                          required=False)
185
 
    name = forms.CharField(max_length=80, label=_("Instance Name"))
186
 
    flavor = forms.ChoiceField(label=_("Flavor"),
187
 
                               help_text=_("Size of image to launch."))
188
 
    count = forms.IntegerField(label=_("Instance Count"),
189
 
                               min_value=1,
190
 
                               initial=1,
191
 
                               help_text=_("Number of instances to launch."))
192
 
 
193
 
    class Meta:
194
140
        name = _("Details")
195
141
        help_text_template = ("project/instances/"
196
142
                              "_launch_details_help.html")
197
143
 
 
144
    def __init__(self, request, context, *args, **kwargs):
 
145
        super(SetInstanceDetailsAction, self).__init__(request,
 
146
                                                       context,
 
147
                                                       *args,
 
148
                                                       **kwargs)
 
149
        choices = [("", _("Select Image"))]
 
150
        try:
 
151
            images = utils.get_available_images(request,
 
152
                                                context.get('project_id'))
 
153
            for image in images:
 
154
                image.bytes = image.size
 
155
                image.volume_size = functions.bytes_to_gigabytes(image.bytes)
 
156
                choices.append((image.id, image))
 
157
            self.fields['image_id'].choices = choices
 
158
        except Exception:
 
159
            exceptions.handle(self.request,
 
160
                              _('Unable to retrieve list of images .'))
 
161
 
198
162
    def clean(self):
199
163
        cleaned_data = super(SetInstanceDetailsAction, self).clean()
200
164
 
 
165
        count = cleaned_data.get('count', 1)
 
166
        # Prevent launching more instances than the quota allows
 
167
        usages = quotas.tenant_quota_usages(self.request)
 
168
        available_count = usages['instances']['available']
 
169
        if available_count < count:
 
170
            error_message = ungettext_lazy('The requested instance '
 
171
                                           'cannot be launched as you only '
 
172
                                           'have %(avail)i of your quota '
 
173
                                           'available. ',
 
174
                                           'The requested %(req)i instances '
 
175
                                           'cannot be launched as you only '
 
176
                                           'have %(avail)i of your quota '
 
177
                                           'available.',
 
178
                                           count)
 
179
            params = {'req': count,
 
180
                      'avail': available_count}
 
181
            raise forms.ValidationError(error_message % params)
 
182
 
201
183
        # Validate our instance source.
202
 
        source = cleaned_data['source_type']
203
 
        # There should always be at least one image_id choice, telling the user
204
 
        # that there are "No Images Available" so we check for 2 here...
205
 
        volume_type = self.data.get('volume_type', None)
206
 
        if volume_type:  # Boot from volume
207
 
            if cleaned_data[source]:
 
184
        source_type = self.data.get('source_type', None)
 
185
 
 
186
        if source_type == 'image_id':
 
187
            if not cleaned_data.get('image_id'):
 
188
                raise forms.ValidationError(_("There are no image sources "
 
189
                                              "available; you must first "
 
190
                                              "create an image before "
 
191
                                              "attemtping to launch an "
 
192
                                              "instance."))
 
193
 
 
194
        elif source_type == 'instance_snapshot_id':
 
195
            if not cleaned_data['instance_snapshot_id']:
 
196
                raise forms.ValidationError(_("There are no snapshot sources "
 
197
                                              "available; you must first "
 
198
                                              "create an snapshot before "
 
199
                                              "attemtping to launch an "
 
200
                                              "instance."))
 
201
 
 
202
        elif source_type == 'volume_id':
 
203
            if not cleaned_data.get('volume_id'):
208
204
                raise forms.ValidationError(_("You can't select an instance "
209
205
                                              "source when booting from a "
210
206
                                              "Volume. The Volume is your "
211
207
                                              "source and should contain "
212
208
                                              "the operating system."))
213
 
        else:  # Boot from image / image_snapshot
214
 
            if source == 'image_id' and not \
215
 
                 filter(lambda x: x[0] != '', self.fields['image_id'].choices):
216
 
                raise forms.ValidationError(_("There are no image sources "
217
 
                                              "available; you must first "
218
 
                                              "create an image before "
219
 
                                              "attemtping to launch an "
220
 
                                              "instance."))
221
 
            elif not cleaned_data[source]:
222
 
                raise forms.ValidationError(_("Please select an option for the"
223
 
                                              " instance source."))
224
 
 
225
 
        # Prevent launching multiple instances with the same volume.
226
 
        # TODO(gabriel): is it safe to launch multiple instances with
227
 
        # a snapshot since it should be cloned to new volumes?
228
 
        count = cleaned_data.get('count', 1)
229
 
        if volume_type and count > 1:
230
 
            msg = _('Launching multiple instances is only supported for '
231
 
                    'images and instance snapshots.')
232
 
            raise forms.ValidationError(msg)
 
209
            # Prevent launching multiple instances with the same volume.
 
210
            # TODO(gabriel): is it safe to launch multiple instances with
 
211
            # a snapshot since it should be cloned to new volumes?
 
212
            if count > 1:
 
213
                msg = _('Launching multiple instances is only supported for '
 
214
                        'images and instance snapshots.')
 
215
                raise forms.ValidationError(msg)
 
216
 
 
217
        elif source_type == 'volume_image_id':
 
218
            if not cleaned_data['image_id']:
 
219
                self._errors[_('volume_image_id')] = [
 
220
                    u"You must select an image."]
 
221
            if not self.data.get('volume_size', None):
 
222
                self._errors['volume_size'] = [_(u"You must set volume size")]
 
223
            if not cleaned_data.get('device_name'):
 
224
                self._errors['device_name'] = [_(u"You must set device name")]
 
225
 
 
226
        elif source_type == 'volume_snapshot_id':
 
227
            if not cleaned_data.get('volume_snapshot_id'):
 
228
                self._errors['volume_snapshot_id'] = [
 
229
                    _(u"You must select a snapshot.")]
 
230
            if not cleaned_data.get('device_name'):
 
231
                self._errors['device_name'] = [_(u"You must set device name")]
233
232
 
234
233
        return cleaned_data
235
234
 
236
 
    def _init_images_cache(self):
237
 
        if not hasattr(self, '_images_cache'):
238
 
            self._images_cache = {}
239
 
 
240
 
    def populate_image_id_choices(self, request, context):
241
 
        self._init_images_cache()
242
 
        images = get_available_images(request, context.get('project_id'),
243
 
                                      self._images_cache)
244
 
        choices = [(image.id, image.name)
245
 
                   for image in images
246
 
                   if image.properties.get("image_type", '') != "snapshot"]
247
 
        if choices:
248
 
            choices.insert(0, ("", _("Select Image")))
249
 
        else:
250
 
            choices.insert(0, ("", _("No images available.")))
251
 
        return choices
252
 
 
253
 
    def populate_instance_snapshot_id_choices(self, request, context):
254
 
        self._init_images_cache()
255
 
        images = get_available_images(request, context.get('project_id'),
256
 
                                      self._images_cache)
257
 
        choices = [(image.id, image.name)
258
 
                   for image in images
259
 
                   if image.properties.get("image_type", '') == "snapshot"]
260
 
        if choices:
261
 
            choices.insert(0, ("", _("Select Instance Snapshot")))
262
 
        else:
263
 
            choices.insert(0, ("", _("No snapshots available.")))
264
 
        return choices
265
 
 
266
235
    def populate_flavor_choices(self, request, context):
 
236
        """By default, returns the available flavors, sorted by RAM
 
237
        usage (ascending).
 
238
        Override these behaviours with a CREATE_INSTANCE_FLAVOR_SORT dict
 
239
        in local_settings.py."""
267
240
        try:
268
241
            flavors = api.nova.flavor_list(request)
 
242
            flavor_sort = getattr(settings, 'CREATE_INSTANCE_FLAVOR_SORT', {})
 
243
            rev = flavor_sort.get('reverse', False)
 
244
            key = flavor_sort.get('key', lambda flavor: flavor.ram)
 
245
 
269
246
            flavor_list = [(flavor.id, "%s" % flavor.name)
270
 
                           for flavor in flavors]
271
 
        except:
 
247
                           for flavor in sorted(flavors, key=key, reverse=rev)]
 
248
        except Exception:
272
249
            flavor_list = []
273
250
            exceptions.handle(request,
274
251
                              _('Unable to retrieve instance flavors.'))
275
 
        return sorted(flavor_list)
 
252
        return flavor_list
276
253
 
277
254
    def populate_availability_zone_choices(self, request, context):
278
255
        try:
279
256
            zones = api.nova.availability_zone_list(request)
280
 
        except:
 
257
        except Exception:
281
258
            zones = []
282
259
            exceptions.handle(request,
283
260
                              _('Unable to retrieve availability zones.'))
297
274
            extra['usages'] = api.nova.tenant_absolute_limits(self.request)
298
275
            extra['usages_json'] = json.dumps(extra['usages'])
299
276
            flavors = json.dumps([f._info for f in
300
 
                                       api.nova.flavor_list(self.request)])
 
277
                                  api.nova.flavor_list(self.request)])
301
278
            extra['flavors'] = flavors
302
 
        except:
 
279
        except Exception:
303
280
            exceptions.handle(self.request,
304
281
                              _("Unable to retrieve quota information."))
305
282
        return super(SetInstanceDetailsAction, self).get_help_text(extra)
306
283
 
 
284
    def _init_images_cache(self):
 
285
        if not hasattr(self, '_images_cache'):
 
286
            self._images_cache = {}
 
287
 
 
288
    def _get_volume_display_name(self, volume):
 
289
        if hasattr(volume, "volume_id"):
 
290
            vol_type = "snap"
 
291
            visible_label = _("Snapshot")
 
292
        else:
 
293
            vol_type = "vol"
 
294
            visible_label = _("Volume")
 
295
        return (("%s:%s" % (volume.id, vol_type)),
 
296
                (_("%(name)s - %(size)s GB (%(label)s)") %
 
297
                 {'name': volume.display_name or volume.id,
 
298
                  'size': volume.size,
 
299
                  'label': visible_label}))
 
300
 
 
301
    def populate_instance_snapshot_id_choices(self, request, context):
 
302
        self._init_images_cache()
 
303
        images = utils.get_available_images(request,
 
304
                                            context.get('project_id'),
 
305
                                            self._images_cache)
 
306
        choices = [(image.id, image.name)
 
307
                   for image in images
 
308
                   if image.properties.get("image_type", '') == "snapshot"]
 
309
        if choices:
 
310
            choices.insert(0, ("", _("Select Instance Snapshot")))
 
311
        else:
 
312
            choices.insert(0, ("", _("No snapshots available.")))
 
313
        return choices
 
314
 
 
315
    def populate_volume_id_choices(self, request, context):
 
316
        volume_options = [("", _("Select Volume"))]
 
317
        try:
 
318
            volumes = [v for v in cinder.volume_list(self.request)
 
319
                       if v.status == api.cinder.VOLUME_STATE_AVAILABLE]
 
320
            volume_options.extend([self._get_volume_display_name(vol)
 
321
                                   for vol in volumes])
 
322
        except Exception:
 
323
            exceptions.handle(self.request,
 
324
                              _('Unable to retrieve list of volumes.'))
 
325
        return volume_options
 
326
 
 
327
    def populate_volume_snapshot_id_choices(self, request, context):
 
328
        volume_options = [("", _("Select Volume Snapshot"))]
 
329
        try:
 
330
            snapshots = cinder.volume_snapshot_list(self.request)
 
331
            snapshots = [s for s in snapshots
 
332
                         if s.status == api.cinder.VOLUME_STATE_AVAILABLE]
 
333
            volume_options.extend([self._get_volume_display_name(snap)
 
334
                                   for snap in snapshots])
 
335
        except Exception:
 
336
            exceptions.handle(self.request,
 
337
                              _('Unable to retrieve list of volume '
 
338
                                'snapshots.'))
 
339
        return volume_options
 
340
 
307
341
 
308
342
class SetInstanceDetails(workflows.Step):
309
343
    action_class = SetInstanceDetailsAction
310
 
    contributes = ("source_type", "source_id", "availability_zone",
311
 
                   "name", "count", "flavor")
 
344
    depends_on = ("project_id", "user_id")
 
345
    contributes = ("source_type", "source_id",
 
346
                   "availability_zone", "name", "count", "flavor",
 
347
                   "device_name",  # Can be None for an image.
 
348
                   "delete_on_terminate")
312
349
 
313
350
    def prepare_action_context(self, request, context):
314
351
        if 'source_type' in context and 'source_id' in context:
324
361
 
325
362
        # Translate form input to context for source values.
326
363
        if "source_type" in data:
327
 
            context["source_id"] = data.get(data['source_type'], None)
 
364
            if data["source_type"] in ["image_id", "volume_image_id"]:
 
365
                context["source_id"] = data.get("image_id", None)
 
366
            else:
 
367
                context["source_id"] = data.get(data["source_type"], None)
 
368
 
 
369
        if "volume_size" in data:
 
370
            context["volume_size"] = data["volume_size"]
328
371
 
329
372
        return context
330
373
 
364
407
        try:
365
408
            keypairs = api.nova.keypair_list(request)
366
409
            keypair_list = [(kp.name, kp.name) for kp in keypairs]
367
 
        except:
 
410
        except Exception:
368
411
            keypair_list = []
369
412
            exceptions.handle(request,
370
413
                              _('Unable to retrieve keypairs.'))
380
423
        try:
381
424
            groups = api.network.security_group_list(request)
382
425
            security_group_list = [(sg.name, sg.name) for sg in groups]
383
 
        except:
 
426
        except Exception:
384
427
            exceptions.handle(request,
385
428
                              _('Unable to retrieve list of security groups'))
386
429
            security_group_list = []
443
486
                                                " be specified.")},
444
487
                                        help_text=_("Launch instance with"
445
488
                                                    " these networks"))
 
489
    if api.neutron.is_port_profiles_supported():
 
490
        profile = forms.ChoiceField(label=_("Policy Profiles"),
 
491
                                    required=False,
 
492
                                    help_text=_("Launch instance with "
 
493
                                                "this policy profile"))
446
494
 
447
495
    class Meta:
448
496
        name = _("Networking")
456
504
            for n in networks:
457
505
                n.set_id_as_name_if_empty()
458
506
            network_list = [(network.id, network.name) for network in networks]
459
 
        except:
 
507
        except Exception:
460
508
            network_list = []
461
509
            exceptions.handle(request,
462
510
                              _('Unable to retrieve networks.'))
463
511
        return network_list
464
512
 
 
513
    def populate_profile_choices(self, request, context):
 
514
        try:
 
515
            profiles = api.neutron.profile_list(request, 'policy')
 
516
            profile_list = [(profile.id, profile.name) for profile in profiles]
 
517
        except Exception:
 
518
            profile_list = []
 
519
            exceptions.handle(request, _("Unable to retrieve profiles."))
 
520
        return profile_list
 
521
 
465
522
 
466
523
class SetNetwork(workflows.Step):
467
524
    action_class = SetNetworkAction
468
 
    template_name = "project/instances/_update_networks.html"
469
 
    contributes = ("network_id",)
 
525
    # Disabling the template drag/drop only in the case port profiles
 
526
    # are used till the issue with the drag/drop affecting the
 
527
    # profile_id detection is fixed.
 
528
    if api.neutron.is_port_profiles_supported():
 
529
        contributes = ("network_id", "profile_id",)
 
530
    else:
 
531
        template_name = "project/instances/_update_networks.html"
 
532
        contributes = ("network_id",)
470
533
 
471
534
    def contribute(self, data, context):
472
535
        if data:
476
539
            networks = [n for n in networks if n != '']
477
540
            if networks:
478
541
                context['network_id'] = networks
 
542
 
 
543
            if api.neutron.is_port_profiles_supported():
 
544
                context['profile_id'] = data.get('profile', None)
479
545
        return context
480
546
 
481
547
 
490
556
                     SetInstanceDetails,
491
557
                     SetAccessControls,
492
558
                     SetNetwork,
493
 
                     VolumeOptions,
494
559
                     PostCreationStep)
495
560
 
496
561
    def format_status_message(self, message):
506
571
    def handle(self, request, context):
507
572
        custom_script = context.get('customization_script', '')
508
573
 
 
574
        dev_mapping_1 = None
 
575
        dev_mapping_2 = None
 
576
 
 
577
        image_id = ''
 
578
 
509
579
        # Determine volume mapping options
510
 
        if context.get('volume_type', None):
511
 
            if(context['delete_on_terminate']):
512
 
                del_on_terminate = 1
513
 
            else:
514
 
                del_on_terminate = 0
515
 
            mapping_opts = ("%s::%s"
516
 
                            % (context['volume_id'], del_on_terminate))
517
 
            dev_mapping = {context['device_name']: mapping_opts}
518
 
        else:
519
 
            dev_mapping = None
 
580
        source_type = context.get('source_type', None)
 
581
        if source_type in ['image_id', 'instance_snapshot_id']:
 
582
            image_id = context['source_id']
 
583
        elif source_type in ['volume_id', 'volume_snapshot_id']:
 
584
            dev_mapping_1 = {context['device_name']: '%s::%s' %
 
585
                                                     (context['source_id'],
 
586
                           int(bool(context['delete_on_terminate'])))}
 
587
        elif source_type == 'volume_image_id':
 
588
            dev_mapping_2 = [
 
589
                {'device_name': str(context['device_name']),
 
590
                 'source_type': 'image',
 
591
                 'destination_type': 'volume',
 
592
                 'delete_on_termination':
 
593
                     int(bool(context['delete_on_terminate'])),
 
594
                 'uuid': context['source_id'],
 
595
                 'boot_index': '0',
 
596
                 'volume_size': context['volume_size']
 
597
                 }
 
598
            ]
520
599
 
521
600
        netids = context.get('network_id', None)
522
601
        if netids:
527
606
 
528
607
        avail_zone = context.get('availability_zone', None)
529
608
 
 
609
        # Create port with Network Name and Port Profile
 
610
        # for the use with the plugin supporting port profiles.
 
611
        # neutron port-create <Network name> --n1kv:profile <Port Profile ID>
 
612
        # for net_id in context['network_id']:
 
613
        ## HACK for now use first network
 
614
        if api.neutron.is_port_profiles_supported():
 
615
            net_id = context['network_id'][0]
 
616
            LOG.debug(_("Horizon->Create Port with %(netid)s %(profile_id)s"),
 
617
                      {'netid': net_id, 'profile_id': context['profile_id']})
 
618
            try:
 
619
                port = api.neutron.port_create(request, net_id,
 
620
                                               policy_profile_id=
 
621
                                               context['profile_id'])
 
622
            except Exception:
 
623
                msg = (_('Port not created for profile-id (%s).') %
 
624
                       context['profile_id'])
 
625
                exceptions.handle(request, msg)
 
626
            if port and port.id:
 
627
                nics = [{"port-id": port.id}]
 
628
 
530
629
        try:
531
630
            api.nova.server_create(request,
532
631
                                   context['name'],
533
 
                                   context['source_id'],
 
632
                                   image_id,
534
633
                                   context['flavor'],
535
634
                                   context['keypair_id'],
536
635
                                   normalize_newlines(custom_script),
537
636
                                   context['security_group_ids'],
538
 
                                   dev_mapping,
 
637
                                   block_device_mapping=dev_mapping_1,
 
638
                                   block_device_mapping_v2=dev_mapping_2,
539
639
                                   nics=nics,
540
640
                                   availability_zone=avail_zone,
541
641
                                   instance_count=int(context['count']),
542
642
                                   admin_pass=context['admin_pass'])
543
643
            return True
544
 
        except:
 
644
        except Exception:
545
645
            exceptions.handle(request)
546
646
            return False