~ubuntu-branches/ubuntu/natty/nova/natty

« back to all changes in this revision

Viewing changes to nova/compute/api.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short, Chuck Short, Soren Hansen
  • Date: 2011-03-31 11:25:10 UTC
  • mfrom: (1.1.14 upstream)
  • Revision ID: james.westby@ubuntu.com-20110331112510-j8imyjc43jpxbe5g
Tags: 2011.2~bzr925-0ubuntu1
[Chuck Short]
* New upstream release.

[Soren Hansen]
* Make the build fail if the test suite does. The test that used to
  fail on the buildd's has been complete rewritten. (LP: #712481)
* Specify that we need Sphinx > 1.0 to build.
* Remove refresh_bzr_branches target from debian/rules. It is not used
  anymore.
* Clean up after doc builds on debian/rules clean.
* Add a nova-ajax-console-proxy package.
* Add Recommends: ajaxterm to nova-compute, so that nova-ajax-console-
  proxy will have something to connect to.
* Stop depending on aoetools. iscsi is the default nowadays (and has
  been for a while).
* Move dependency on open-iscsi from nova-volume to nova-compute.
  They're client tools, so that's where they belong.
* Add a build-depends on python-suds.
* Add logrote config for nova-ajax-console-proxy.
* Add upstart job for nova-ajax-console-proxy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
from nova import utils
35
35
from nova import volume
36
36
from nova.compute import instance_types
 
37
from nova.scheduler import api as scheduler_api
37
38
from nova.db import base
38
39
 
39
40
FLAGS = flags.FLAGS
253
254
 
254
255
        return [dict(x.iteritems()) for x in instances]
255
256
 
 
257
    def has_finished_migration(self, context, instance_id):
 
258
        """Retrieves whether or not a finished migration exists for
 
259
        an instance"""
 
260
        try:
 
261
            db.migration_get_by_instance_and_status(context, instance_id,
 
262
                    'finished')
 
263
            return True
 
264
        except exception.NotFound:
 
265
            return False
 
266
 
256
267
    def ensure_default_security_group(self, context):
257
268
        """ Create security group for the security context if it
258
269
        does not already exist
342
353
        rv = self.db.instance_update(context, instance_id, kwargs)
343
354
        return dict(rv.iteritems())
344
355
 
 
356
    @scheduler_api.reroute_compute("delete")
345
357
    def delete(self, context, instance_id):
346
358
        LOG.debug(_("Going to try to terminate %s"), instance_id)
347
359
        try:
374
386
        rv = self.db.instance_get(context, instance_id)
375
387
        return dict(rv.iteritems())
376
388
 
 
389
    @scheduler_api.reroute_compute("get")
 
390
    def routing_get(self, context, instance_id):
 
391
        """Use this method instead of get() if this is the only
 
392
           operation you intend to to. It will route to novaclient.get
 
393
           if the instance is not found."""
 
394
        return self.get(context, instance_id)
 
395
 
377
396
    def get_all(self, context, project_id=None, reservation_id=None,
378
397
                fixed_ip=None):
379
398
        """Get all instances, possibly filtered by one of the
380
399
        given parameters. If there is no filter and the context is
381
 
        an admin, it will retreive all instances in the system."""
 
400
        an admin, it will retreive all instances in the system.
 
401
        """
382
402
        if reservation_id is not None:
383
 
            return self.db.instance_get_all_by_reservation(context,
384
 
                                                             reservation_id)
 
403
            return self.db.instance_get_all_by_reservation(
 
404
                context, reservation_id)
 
405
 
385
406
        if fixed_ip is not None:
386
407
            return self.db.fixed_ip_get_instance(context, fixed_ip)
 
408
 
387
409
        if project_id or not context.is_admin:
388
410
            if not context.project:
389
 
                return self.db.instance_get_all_by_user(context,
390
 
                                                        context.user_id)
 
411
                return self.db.instance_get_all_by_user(
 
412
                    context, context.user_id)
 
413
 
391
414
            if project_id is None:
392
415
                project_id = context.project_id
393
 
            return self.db.instance_get_all_by_project(context,
394
 
            project_id)
 
416
 
 
417
            return self.db.instance_get_all_by_project(
 
418
                context, project_id)
 
419
 
395
420
        return self.db.instance_get_all(context)
396
421
 
397
422
    def _cast_compute_message(self, method, context, instance_id, host=None,
441
466
 
442
467
        :retval: A dict containing image metadata
443
468
        """
444
 
        data = {'name': name, 'is_public': False}
445
 
        image_meta = self.image_service.create(context, data)
446
 
        params = {'image_id': image_meta['id']}
 
469
        properties = {'instance_id': str(instance_id),
 
470
                      'user_id': str(context.user_id)}
 
471
        sent_meta = {'name': name, 'is_public': False,
 
472
                     'properties': properties}
 
473
        recv_meta = self.image_service.create(context, sent_meta)
 
474
        params = {'image_id': recv_meta['id']}
447
475
        self._cast_compute_message('snapshot_instance', context, instance_id,
448
476
                                   params=params)
449
 
        return image_meta
 
477
        return recv_meta
450
478
 
451
479
    def reboot(self, context, instance_id):
452
480
        """Reboot the given instance."""
464
492
        params = {'migration_id': migration_ref['id']}
465
493
        self._cast_compute_message('revert_resize', context, instance_id,
466
494
                migration_ref['dest_compute'], params=params)
 
495
        self.db.migration_update(context, migration_ref['id'],
 
496
                {'status': 'reverted'})
467
497
 
468
498
    def confirm_resize(self, context, instance_id):
469
499
        """Confirms a migration/resize, deleting the 'old' instance in the
479
509
        self._cast_compute_message('confirm_resize', context, instance_id,
480
510
                migration_ref['source_compute'], params=params)
481
511
 
482
 
        self.db.migration_update(context, migration_id,
 
512
        self.db.migration_update(context, migration_ref['id'],
483
513
                {'status': 'confirmed'})
484
514
        self.db.instance_update(context, instance_id,
485
515
                {'host': migration_ref['dest_compute'], })
486
516
 
487
 
    def resize(self, context, instance_id, flavor):
 
517
    def resize(self, context, instance_id, flavor_id):
488
518
        """Resize a running instance."""
 
519
        instance = self.db.instance_get(context, instance_id)
 
520
        current_instance_type = self.db.instance_type_get_by_name(
 
521
            context, instance['instance_type'])
 
522
 
 
523
        new_instance_type = self.db.instance_type_get_by_flavor_id(
 
524
                context, flavor_id)
 
525
        current_instance_type_name = current_instance_type['name']
 
526
        new_instance_type_name = new_instance_type['name']
 
527
        LOG.debug(_("Old instance type %(current_instance_type_name)s, "
 
528
                " new instance type %(new_instance_type_name)s") % locals())
 
529
        if not new_instance_type:
 
530
            raise exception.ApiError(_("Requested flavor %(flavor_id)d "
 
531
                    "does not exist") % locals())
 
532
 
 
533
        current_memory_mb = current_instance_type['memory_mb']
 
534
        new_memory_mb = new_instance_type['memory_mb']
 
535
        if current_memory_mb > new_memory_mb:
 
536
            raise exception.ApiError(_("Invalid flavor: cannot downsize"
 
537
                    "instances"))
 
538
        if current_memory_mb == new_memory_mb:
 
539
            raise exception.ApiError(_("Invalid flavor: cannot use"
 
540
                    "the same flavor. "))
 
541
 
489
542
        self._cast_scheduler_message(context,
490
543
                    {"method": "prep_resize",
491
544
                     "args": {"topic": FLAGS.compute_topic,
492
 
                              "instance_id": instance_id, }},)
 
545
                              "instance_id": instance_id,
 
546
                              "flavor_id": flavor_id}})
493
547
 
 
548
    @scheduler_api.reroute_compute("pause")
494
549
    def pause(self, context, instance_id):
495
550
        """Pause the given instance."""
496
551
        self._cast_compute_message('pause_instance', context, instance_id)
497
552
 
 
553
    @scheduler_api.reroute_compute("unpause")
498
554
    def unpause(self, context, instance_id):
499
555
        """Unpause the given instance."""
500
556
        self._cast_compute_message('unpause_instance', context, instance_id)
501
557
 
 
558
    @scheduler_api.reroute_compute("diagnostics")
502
559
    def get_diagnostics(self, context, instance_id):
503
560
        """Retrieve diagnostics for the given instance."""
504
561
        return self._call_compute_message(
510
567
        """Retrieve actions for the given instance."""
511
568
        return self.db.instance_get_actions(context, instance_id)
512
569
 
 
570
    @scheduler_api.reroute_compute("suspend")
513
571
    def suspend(self, context, instance_id):
514
572
        """suspend the instance with instance_id"""
515
573
        self._cast_compute_message('suspend_instance', context, instance_id)
516
574
 
 
575
    @scheduler_api.reroute_compute("resume")
517
576
    def resume(self, context, instance_id):
518
577
        """resume the instance with instance_id"""
519
578
        self._cast_compute_message('resume_instance', context, instance_id)
520
579
 
 
580
    @scheduler_api.reroute_compute("rescue")
521
581
    def rescue(self, context, instance_id):
522
582
        """Rescue the given instance."""
523
583
        self._cast_compute_message('rescue_instance', context, instance_id)
524
584
 
 
585
    @scheduler_api.reroute_compute("unrescue")
525
586
    def unrescue(self, context, instance_id):
526
587
        """Unrescue the given instance."""
527
588
        self._cast_compute_message('unrescue_instance', context, instance_id)
537
598
 
538
599
    def get_ajax_console(self, context, instance_id):
539
600
        """Get a url to an AJAX Console"""
540
 
        instance = self.get(context, instance_id)
541
601
        output = self._call_compute_message('get_ajax_console',
542
602
                                            context,
543
603
                                            instance_id)
548
608
        return {'url': '%s/?token=%s' % (FLAGS.ajax_console_proxy_url,
549
609
                output['token'])}
550
610
 
 
611
    def get_vnc_console(self, context, instance_id):
 
612
        """Get a url to a VNC Console."""
 
613
        instance = self.get(context, instance_id)
 
614
        output = self._call_compute_message('get_vnc_console',
 
615
                                            context,
 
616
                                            instance_id)
 
617
        rpc.call(context, '%s' % FLAGS.vncproxy_topic,
 
618
                 {'method': 'authorize_vnc_console',
 
619
                  'args': {'token': output['token'],
 
620
                           'host': output['host'],
 
621
                           'port': output['port']}})
 
622
 
 
623
        # hostignore and portignore are compatability params for noVNC
 
624
        return {'url': '%s/vnc_auto.html?token=%s&host=%s&port=%s' % (
 
625
                       FLAGS.vncproxy_url,
 
626
                       output['token'],
 
627
                       'hostignore',
 
628
                       'portignore')}
 
629
 
551
630
    def get_console_output(self, context, instance_id):
552
631
        """Get console output for an an instance"""
553
632
        return self._call_compute_message('get_console_output',
585
664
        if not re.match("^/dev/[a-z]d[a-z]+$", device):
586
665
            raise exception.ApiError(_("Invalid device specified: %s. "
587
666
                                     "Example device: /dev/vdb") % device)
588
 
        self.volume_api.check_attach(context, volume_id)
 
667
        self.volume_api.check_attach(context, volume_id=volume_id)
589
668
        instance = self.get(context, instance_id)
590
669
        host = instance['host']
591
670
        rpc.cast(context,
599
678
        instance = self.db.volume_get_instance(context.elevated(), volume_id)
600
679
        if not instance:
601
680
            raise exception.ApiError(_("Volume isn't attached to anything!"))
602
 
        self.volume_api.check_detach(context, volume_id)
 
681
        self.volume_api.check_detach(context, volume_id=volume_id)
603
682
        host = instance['host']
604
683
        rpc.cast(context,
605
684
                 self.db.queue_get_for(context, FLAGS.compute_topic, host),
610
689
 
611
690
    def associate_floating_ip(self, context, instance_id, address):
612
691
        instance = self.get(context, instance_id)
613
 
        self.network_api.associate_floating_ip(context, address,
614
 
                                               instance['fixed_ip'])
 
692
        self.network_api.associate_floating_ip(context,
 
693
                                               floating_ip=address,
 
694
                                               fixed_ip=instance['fixed_ip'])
 
695
 
 
696
    def get_instance_metadata(self, context, instance_id):
 
697
        """Get all metadata associated with an instance."""
 
698
        rv = self.db.instance_metadata_get(context, instance_id)
 
699
        return dict(rv.iteritems())
 
700
 
 
701
    def delete_instance_metadata(self, context, instance_id, key):
 
702
        """Delete the given metadata item"""
 
703
        self.db.instance_metadata_delete(context, instance_id, key)
 
704
 
 
705
    def update_or_create_instance_metadata(self, context, instance_id,
 
706
                                            metadata):
 
707
        """Updates or creates instance metadata"""
 
708
        self.db.instance_metadata_update_or_create(context, instance_id,
 
709
                                                    metadata)