~ubuntu-branches/ubuntu/raring/nova/raring-proposed

« back to all changes in this revision

Viewing changes to nova/virt/hyperv/vmops.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Adam Gandelman, Chuck Short
  • Date: 2012-11-23 09:04:58 UTC
  • mfrom: (1.1.66)
  • Revision ID: package-import@ubuntu.com-20121123090458-91565o7aev1i1h71
Tags: 2013.1~g1-0ubuntu1
[ Adam Gandelman ]
* debian/control: Ensure novaclient is upgraded with nova,
  require python-keystoneclient >= 1:2.9.0. (LP: #1073289)
* debian/patches/{ubuntu/*, rbd-security.patch}: Dropped, applied
  upstream.
* debian/control: Add python-testtools to Build-Depends.

[ Chuck Short ]
* New upstream version.
* Refreshed debian/patches/avoid_setuptools_git_dependency.patch.
* debian/rules: FTBFS if missing binaries.
* debian/nova-scheudler.install: Add missing rabbit-queues and
  nova-rpc-zmq-receiver.
* Remove nova-volume since it doesnt exist anymore, transition to cinder-*.
* debian/rules: install apport hook in the right place.
* debian/patches/ubuntu-show-tests.patch: Display test failures.
* debian/control: Add depends on genisoimage
* debian/control: Suggest guestmount.
* debian/control: Suggest websockify. (LP: #1076442)
* debian/nova.conf: Disable nova-volume service.
* debian/control: Depend on xen-system-* rather than the hypervisor.
* debian/control, debian/mans/nova-conductor.8, debian/nova-conductor.init,
  debian/nova-conductor.install, debian/nova-conductor.logrotate
  debian/nova-conductor.manpages, debian/nova-conductor.postrm
  debian/nova-conductor.upstart.in: Add nova-conductor service.
* debian/control: Add python-fixtures as a build deps.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
"""
19
19
Management class for basic VM operations.
20
20
"""
21
 
import multiprocessing
22
21
import os
23
 
import platform
24
22
import uuid
25
23
 
26
 
from nova import db
 
24
from nova.api.metadata import base as instance_metadata
27
25
from nova import exception
28
 
from nova import flags
29
26
from nova.openstack.common import cfg
 
27
from nova.openstack.common import lockutils
30
28
from nova.openstack.common import log as logging
31
29
from nova import utils
 
30
from nova.virt import configdrive
32
31
from nova.virt.hyperv import baseops
33
32
from nova.virt.hyperv import constants
34
33
from nova.virt.hyperv import vmutils
42
41
                    'if none provided first external is used'),
43
42
    cfg.BoolOpt('limit_cpu_features',
44
43
               default=False,
45
 
               help='required for live migration among '
46
 
                    'hosts with different CPU features')
 
44
               help='Required for live migration among '
 
45
                    'hosts with different CPU features'),
 
46
    cfg.BoolOpt('config_drive_inject_password',
 
47
               default=False,
 
48
               help='Sets the admin password in the config drive image'),
 
49
    cfg.StrOpt('qemu_img_cmd',
 
50
               default="qemu-img.exe",
 
51
               help='qemu-img is used to convert between '
 
52
                    'different image types'),
 
53
    cfg.BoolOpt('config_drive_cdrom',
 
54
               default=False,
 
55
               help='Attaches the Config Drive image as a cdrom drive '
 
56
                    'instead of a disk drive')
47
57
    ]
48
58
 
49
 
FLAGS = flags.FLAGS
50
 
FLAGS.register_opts(hyperv_opts)
 
59
CONF = cfg.CONF
 
60
CONF.register_opts(hyperv_opts)
 
61
CONF.import_opt('use_cow_images', 'nova.config')
51
62
 
52
63
 
53
64
class VMOps(baseops.BaseOps):
91
102
        info = summary_info[0]
92
103
 
93
104
        LOG.debug(_("hyperv vm state: %s"), info.EnabledState)
94
 
        state = str(constants.HYPERV_POWER_STATE[info.EnabledState])
 
105
        state = constants.HYPERV_POWER_STATE[info.EnabledState]
95
106
        memusage = str(info.MemoryUsage)
96
107
        numprocs = str(info.NumberOfProcessors)
97
108
        uptime = str(info.UpTime)
98
109
 
99
 
        LOG.debug(_("Got Info for vm %(instance_name)s: state=%(state)s,"
 
110
        LOG.debug(_("Got Info for vm %(instance_name)s: state=%(state)d,"
100
111
                " mem=%(memusage)s, num_cpu=%(numprocs)s,"
101
112
                " uptime=%(uptime)s"), locals())
102
113
 
106
117
                'num_cpu': info.NumberOfProcessors,
107
118
                'cpu_time': info.UpTime}
108
119
 
109
 
    def spawn(self, context, instance, image_meta, network_info,
110
 
        block_device_info=None):
 
120
    def spawn(self, context, instance, image_meta, injected_files,
 
121
        admin_password, network_info, block_device_info=None):
111
122
        """ Create a new VM and start it."""
112
123
        instance_name = instance["name"]
113
124
        vm = self._vmutils.lookup(self._conn, instance_name)
130
141
                  image_id=instance['image_ref'],
131
142
                  user=instance['user_id'],
132
143
                  project=instance['project_id'],
133
 
                  cow=FLAGS.use_cow_images)
 
144
                  cow=CONF.use_cow_images)
134
145
            except Exception as exn:
135
146
                LOG.exception(_('cache image failed: %s'), exn)
136
147
                self.destroy(instance)
139
150
            self._create_vm(instance)
140
151
 
141
152
            if not ebs_root:
142
 
                self._create_disk(instance['name'], vhdfile)
 
153
                self._attach_ide_drive(instance['name'], vhdfile, 0, 0,
 
154
                    constants.IDE_DISK)
143
155
            else:
144
156
                self._volumeops.attach_boot_volume(block_device_info,
145
157
                                             instance_name)
151
163
                mac_address = vif['address'].replace(':', '')
152
164
                self._create_nic(instance['name'], mac_address)
153
165
 
 
166
            if configdrive.required_by(instance):
 
167
                self._create_config_drive(instance, injected_files,
 
168
                    admin_password)
 
169
 
154
170
            LOG.debug(_('Starting VM %s '), instance_name)
155
171
            self._set_vm_state(instance['name'], 'Enabled')
156
172
            LOG.info(_('Started VM %s '), instance_name)
159
175
            self.destroy(instance)
160
176
            raise exn
161
177
 
 
178
    def _create_config_drive(self, instance, injected_files, admin_password):
 
179
        if CONF.config_drive_format != 'iso9660':
 
180
            vmutils.HyperVException(_('Invalid config_drive_format "%s"') %
 
181
                CONF.config_drive_format)
 
182
 
 
183
        LOG.info(_('Using config drive'), instance=instance)
 
184
        extra_md = {}
 
185
        if admin_password and CONF.config_drive_inject_password:
 
186
            extra_md['admin_pass'] = admin_password
 
187
 
 
188
        inst_md = instance_metadata.InstanceMetadata(instance,
 
189
            content=injected_files, extra_md=extra_md)
 
190
 
 
191
        instance_path = self._vmutils.get_instance_path(
 
192
            instance['name'])
 
193
        configdrive_path_iso = os.path.join(instance_path, 'configdrive.iso')
 
194
        LOG.info(_('Creating config drive at %(path)s'),
 
195
                 {'path': configdrive_path_iso}, instance=instance)
 
196
 
 
197
        cdb = configdrive.ConfigDriveBuilder(instance_md=inst_md)
 
198
        try:
 
199
            cdb.make_drive(configdrive_path_iso)
 
200
        finally:
 
201
            cdb.cleanup()
 
202
 
 
203
        if not CONF.config_drive_cdrom:
 
204
            drive_type = constants.IDE_DISK
 
205
            configdrive_path = os.path.join(instance_path,
 
206
                'configdrive.vhd')
 
207
            utils.execute(CONF.qemu_img_cmd,
 
208
                          'convert',
 
209
                          '-f',
 
210
                          'raw',
 
211
                          '-O',
 
212
                          'vpc',
 
213
                          configdrive_path_iso,
 
214
                          configdrive_path,
 
215
                          attempts=1)
 
216
            os.remove(configdrive_path_iso)
 
217
        else:
 
218
            drive_type = constants.IDE_DVD
 
219
            configdrive_path = configdrive_path_iso
 
220
 
 
221
        self._attach_ide_drive(instance['name'], configdrive_path, 1, 0,
 
222
            drive_type)
 
223
 
162
224
    def _create_vm(self, instance):
163
225
        """Create a VM but don't start it.  """
164
226
        instance_name = instance["name"]
202
264
        procsetting.Reservation = vcpus
203
265
        procsetting.Limit = 100000  # static assignment to 100%
204
266
 
205
 
        if FLAGS.limit_cpu_features:
 
267
        if CONF.limit_cpu_features:
206
268
            procsetting.LimitProcessorFeatures = True
207
269
 
208
270
        (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources(
231
293
                _('Failed to add scsi controller to VM %s') %
232
294
                vm_name)
233
295
 
234
 
    def _create_disk(self, vm_name, vhdfile):
235
 
        """Create a disk and attach it to the vm"""
236
 
        LOG.debug(_('Creating disk for %(vm_name)s by attaching'
237
 
                ' disk file %(vhdfile)s') % locals())
 
296
    def _get_ide_controller(self, vm, ctrller_addr):
238
297
        #Find the IDE controller for the vm.
239
 
        vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name)
240
 
        vm = vms[0]
241
298
        vmsettings = vm.associators(
242
299
            wmi_result_class='Msvm_VirtualSystemSettingData')
243
300
        rasds = vmsettings[0].associators(
244
301
            wmi_result_class='MSVM_ResourceAllocationSettingData')
245
302
        ctrller = [r for r in rasds
246
303
            if r.ResourceSubType == 'Microsoft Emulated IDE Controller'
247
 
            and r.Address == "0"]
 
304
            and r.Address == str(ctrller_addr)]
 
305
        return ctrller
 
306
 
 
307
    def _attach_ide_drive(self, vm_name, path, ctrller_addr, drive_addr,
 
308
        drive_type=constants.IDE_DISK):
 
309
        """Create an IDE drive and attach it to the vm"""
 
310
        LOG.debug(_('Creating disk for %(vm_name)s by attaching'
 
311
                ' disk file %(path)s') % locals())
 
312
 
 
313
        vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name)
 
314
        vm = vms[0]
 
315
 
 
316
        ctrller = self._get_ide_controller(vm, ctrller_addr)
 
317
 
 
318
        if drive_type == constants.IDE_DISK:
 
319
            resSubType = 'Microsoft Synthetic Disk Drive'
 
320
        elif drive_type == constants.IDE_DVD:
 
321
            resSubType = 'Microsoft Synthetic DVD Drive'
 
322
 
248
323
        #Find the default disk drive object for the vm and clone it.
249
 
        diskdflt = self._conn.query(
 
324
        drivedflt = self._conn.query(
250
325
            "SELECT * FROM Msvm_ResourceAllocationSettingData \
251
 
            WHERE ResourceSubType LIKE 'Microsoft Synthetic Disk Drive'\
252
 
            AND InstanceID LIKE '%Default%'")[0]
253
 
        diskdrive = self._vmutils.clone_wmi_obj(self._conn,
254
 
                'Msvm_ResourceAllocationSettingData', diskdflt)
 
326
            WHERE ResourceSubType LIKE '%(resSubType)s'\
 
327
            AND InstanceID LIKE '%%Default%%'" % locals())[0]
 
328
        drive = self._vmutils.clone_wmi_obj(self._conn,
 
329
                'Msvm_ResourceAllocationSettingData', drivedflt)
255
330
        #Set the IDE ctrller as parent.
256
 
        diskdrive.Parent = ctrller[0].path_()
257
 
        diskdrive.Address = 0
 
331
        drive.Parent = ctrller[0].path_()
 
332
        drive.Address = drive_addr
258
333
        #Add the cloned disk drive object to the vm.
259
334
        new_resources = self._vmutils.add_virt_resource(self._conn,
260
 
            diskdrive, vm)
 
335
            drive, vm)
261
336
        if new_resources is None:
262
337
            raise vmutils.HyperVException(
263
 
                _('Failed to add diskdrive to VM %s') %
 
338
                _('Failed to add drive to VM %s') %
264
339
                    vm_name)
265
 
        diskdrive_path = new_resources[0]
266
 
        LOG.debug(_('New disk drive path is %s'), diskdrive_path)
 
340
        drive_path = new_resources[0]
 
341
        LOG.debug(_('New %(drive_type)s drive path is %(drive_path)s') %
 
342
            locals())
 
343
 
 
344
        if drive_type == constants.IDE_DISK:
 
345
            resSubType = 'Microsoft Virtual Hard Disk'
 
346
        elif drive_type == constants.IDE_DVD:
 
347
            resSubType = 'Microsoft Virtual CD/DVD Disk'
 
348
 
267
349
        #Find the default VHD disk object.
268
 
        vhddefault = self._conn.query(
 
350
        drivedefault = self._conn.query(
269
351
                "SELECT * FROM Msvm_ResourceAllocationSettingData \
270
 
                 WHERE ResourceSubType LIKE 'Microsoft Virtual Hard Disk' AND \
271
 
                 InstanceID LIKE '%Default%' ")[0]
 
352
                 WHERE ResourceSubType LIKE '%(resSubType)s' AND \
 
353
                 InstanceID LIKE '%%Default%%' " % locals())[0]
272
354
 
273
355
        #Clone the default and point it to the image file.
274
 
        vhddisk = self._vmutils.clone_wmi_obj(self._conn,
275
 
                'Msvm_ResourceAllocationSettingData', vhddefault)
 
356
        res = self._vmutils.clone_wmi_obj(self._conn,
 
357
                'Msvm_ResourceAllocationSettingData', drivedefault)
276
358
        #Set the new drive as the parent.
277
 
        vhddisk.Parent = diskdrive_path
278
 
        vhddisk.Connection = [vhdfile]
 
359
        res.Parent = drive_path
 
360
        res.Connection = [path]
279
361
 
280
362
        #Add the new vhd object as a virtual hard disk to the vm.
281
 
        new_resources = self._vmutils.add_virt_resource(self._conn,
282
 
            vhddisk, vm)
 
363
        new_resources = self._vmutils.add_virt_resource(self._conn, res, vm)
283
364
        if new_resources is None:
284
365
            raise vmutils.HyperVException(
285
 
                _('Failed to add vhd file to VM %s') %
286
 
                    vm_name)
287
 
        LOG.info(_('Created disk for %s'), vm_name)
 
366
                _('Failed to add %(drive_type)s image to VM %(vm_name)s') %
 
367
                    locals())
 
368
        LOG.info(_('Created drive type %(drive_type)s for %(vm_name)s') %
 
369
            locals())
288
370
 
289
371
    def _create_nic(self, vm_name, mac):
290
372
        """Create a (synthetic) nic and attach it to the vm"""
339
421
        """
340
422
        #If there are no physical nics connected to networks, return.
341
423
        LOG.debug(_("Attempting to bind NIC to %s ")
342
 
                % FLAGS.vswitch_name)
343
 
        if FLAGS.vswitch_name:
 
424
                % CONF.vswitch_name)
 
425
        if CONF.vswitch_name:
344
426
            LOG.debug(_("Attempting to bind NIC to %s ")
345
 
                % FLAGS.vswitch_name)
 
427
                % CONF.vswitch_name)
346
428
            bound = self._conn.Msvm_VirtualSwitch(
347
 
                ElementName=FLAGS.vswitch_name)
 
429
                ElementName=CONF.vswitch_name)
348
430
        else:
349
431
            LOG.debug(_("No vSwitch specified, attaching to default"))
350
432
            self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')
351
433
        if len(bound) == 0:
352
434
            return None
353
 
        if FLAGS.vswitch_name:
 
435
        if CONF.vswitch_name:
354
436
            return self._conn.Msvm_VirtualSwitch(
355
 
                ElementName=FLAGS.vswitch_name)[0]\
 
437
                ElementName=CONF.vswitch_name)[0]\
356
438
                .associators(wmi_result_class='Msvm_SwitchPort')[0]\
357
439
                .associators(wmi_result_class='Msvm_VirtualSwitch')[0]
358
440
        else:
475
557
            LOG.error(msg)
476
558
            raise vmutils.HyperVException(msg)
477
559
 
478
 
    def _get_vcpu_total(self):
479
 
        """Get vcpu number of physical computer.
480
 
        :returns: the number of cpu core.
481
 
        """
482
 
        # On certain platforms, this will raise a NotImplementedError.
483
 
        try:
484
 
            return multiprocessing.cpu_count()
485
 
        except NotImplementedError:
486
 
            LOG.warn(_("Cannot get the number of cpu, because this "
487
 
                       "function is not implemented for this platform. "
488
 
                       "This error can be safely ignored for now."))
489
 
            return 0
490
 
 
491
 
    def _get_memory_mb_total(self):
492
 
        """Get the total memory size(MB) of physical computer.
493
 
        :returns: the total amount of memory(MB).
494
 
        """
495
 
        total_kb = self._conn_cimv2.query(
496
 
            "SELECT TotalVisibleMemorySize FROM win32_operatingsystem")[0]\
497
 
            .TotalVisibleMemorySize
498
 
        total_mb = long(total_kb) / 1024
499
 
        return total_mb
500
 
 
501
 
    def _get_local_gb_total(self):
502
 
        """Get the total hdd size(GB) of physical computer.
503
 
        :returns:
504
 
            The total amount of HDD(GB).
505
 
            Note that this value shows a partition where
506
 
            NOVA-INST-DIR/instances mounts.
507
 
        """
508
 
        #TODO(jordanrinke): This binds to C only right now,
509
 
        #need to bind to instance dir
510
 
        total_kb = self._conn_cimv2.query(
511
 
            "SELECT Size FROM win32_logicaldisk WHERE DriveType=3")[0].Size
512
 
        total_gb = long(total_kb) / (1024 ** 3)
513
 
        return total_gb
514
 
 
515
 
    def _get_vcpu_used(self):
516
 
        """ Get vcpu usage number of physical computer.
517
 
        :returns: The total number of vcpu that currently used.
518
 
        """
519
 
        #TODO(jordanrinke) figure out a way to count assigned VCPUs
520
 
        total_vcpu = 0
521
 
        return total_vcpu
522
 
 
523
 
    def _get_memory_mb_used(self):
524
 
        """Get the free memory size(MB) of physical computer.
525
 
        :returns: the total usage of memory(MB).
526
 
        """
527
 
        total_kb = self._conn_cimv2.query(
528
 
            "SELECT FreePhysicalMemory FROM win32_operatingsystem")[0]\
529
 
                .FreePhysicalMemory
530
 
        total_mb = long(total_kb) / 1024
531
 
 
532
 
        return total_mb
533
 
 
534
 
    def _get_local_gb_used(self):
535
 
        """Get the free hdd size(GB) of physical computer.
536
 
        :returns:
537
 
           The total usage of HDD(GB).
538
 
           Note that this value shows a partition where
539
 
           NOVA-INST-DIR/instances mounts.
540
 
        """
541
 
        #TODO(jordanrinke): This binds to C only right now,
542
 
        #need to bind to instance dir
543
 
        total_kb = self._conn_cimv2.query(
544
 
            "SELECT FreeSpace FROM win32_logicaldisk WHERE DriveType=3")[0]\
545
 
                .FreeSpace
546
 
        total_gb = long(total_kb) / (1024 ** 3)
547
 
        return total_gb
548
 
 
549
 
    def _get_hypervisor_version(self):
550
 
        """Get hypervisor version.
551
 
        :returns: hypervisor version (ex. 12003)
552
 
        """
553
 
        version = self._conn_cimv2.Win32_OperatingSystem()[0]\
554
 
            .Version.replace('.', '')
555
 
        LOG.info(_('Windows version: %s ') % version)
556
 
        return version
557
 
 
558
 
    def get_available_resource(self):
559
 
        """Retrieve resource info.
560
 
 
561
 
        This method is called when nova-compute launches, and
562
 
        as part of a periodic task.
563
 
 
564
 
        :returns: dictionary describing resources
565
 
 
566
 
        """
567
 
        LOG.info(_('get_available_resource called'))
568
 
 
569
 
        # TODO(alexpilotti) implemented cpu_info
570
 
        dic = {'vcpus': self._get_vcpu_total(),
571
 
               'memory_mb': self._get_memory_mb_total(),
572
 
               'local_gb': self._get_local_gb_total(),
573
 
               'vcpus_used': self._get_vcpu_used(),
574
 
               'memory_mb_used': self._get_memory_mb_used(),
575
 
               'local_gb_used': self._get_local_gb_used(),
576
 
               'hypervisor_type': "hyperv",
577
 
               'hypervisor_version': self._get_hypervisor_version(),
578
 
               'hypervisor_hostname': platform.node(),
579
 
               'cpu_info': 'unknown'}
580
 
 
581
 
        return dic
582
 
 
583
560
    def _cache_image(self, fn, target, fname, cow=False, Size=None,
584
561
        *args, **kwargs):
585
562
        """Wrapper for a method that creates an image that caches the image.
595
572
 
596
573
        If cow is True, it will make a CoW image instead of a copy.
597
574
        """
598
 
        @utils.synchronized(fname)
 
575
        @lockutils.synchronized(fname, 'nova-')
599
576
        def call_if_not_exists(path, fn, *args, **kwargs):
600
577
                if not os.path.exists(path):
601
578
                    fn(target=path, *args, **kwargs)