681 attached to the instance. |
681 attached to the instance. |
682 :param preserve_ephemeral: True if the default ephemeral storage |
682 :param preserve_ephemeral: True if the default ephemeral storage |
683 partition must be preserved on rebuild |
683 partition must be preserved on rebuild |
684 """ |
684 """ |
685 raise NotImplementedError() |
685 raise NotImplementedError() |
|
686 |
|
687 def _get_extra_specs(self, instance): |
|
688 """Retrieve extra_specs of an instance.""" |
|
689 flavor = flavor_obj.Flavor.get_by_id( |
|
690 nova_context.get_admin_context(read_deleted='yes'), |
|
691 instance['instance_type_id']) |
|
692 return flavor['extra_specs'].copy() |
686 |
693 |
687 def _fetch_image(self, context, instance): |
694 def _fetch_image(self, context, instance): |
688 """Fetch an image using Glance given the instance's image_ref.""" |
695 """Fetch an image using Glance given the instance's image_ref.""" |
689 glancecache_dirname = CONF.glancecache_dirname |
696 glancecache_dirname = CONF.glancecache_dirname |
690 fileutils.ensure_tree(glancecache_dirname) |
697 fileutils.ensure_tree(glancecache_dirname) |
877 except Exception as reason: |
884 except Exception as reason: |
878 LOG.error(_("Unable to create root zpool volume for instance '%s'" |
885 LOG.error(_("Unable to create root zpool volume for instance '%s'" |
879 ": %s") % (instance['name'], reason)) |
886 ": %s") % (instance['name'], reason)) |
880 raise |
887 raise |
881 |
888 |
882 def _connect_boot_volume(self, volume, mountpoint, context, instance, |
889 def _connect_boot_volume(self, volume, mountpoint, context, instance): |
883 extra_specs): |
|
884 """Connect a (Cinder) volume service backed boot volume""" |
890 """Connect a (Cinder) volume service backed boot volume""" |
885 brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS) |
|
886 instance_uuid = instance['uuid'] |
891 instance_uuid = instance['uuid'] |
887 volume_id = volume['id'] |
892 volume_id = volume['id'] |
888 |
893 |
889 connector = self.get_volume_connector(instance) |
894 connector = self.get_volume_connector(instance) |
890 connection_info = self._volume_api.initialize_connection( |
895 connection_info = self._volume_api.initialize_connection( |
892 |
897 |
893 # Check connection_info to determine if the provided volume is |
898 # Check connection_info to determine if the provided volume is |
894 # local to this compute node. If it is, then don't use it for |
899 # local to this compute node. If it is, then don't use it for |
895 # Solaris branded zones in order to avoid a known ZFS deadlock issue |
900 # Solaris branded zones in order to avoid a known ZFS deadlock issue |
896 # when using a zpool within another zpool on the same system. |
901 # when using a zpool within another zpool on the same system. |
|
902 extra_specs = self._get_extra_specs(instance) |
|
903 brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS) |
897 if brand == ZONE_BRAND_SOLARIS: |
904 if brand == ZONE_BRAND_SOLARIS: |
898 driver_type = connection_info['driver_volume_type'] |
905 driver_type = connection_info['driver_volume_type'] |
899 if driver_type == 'local': |
906 if driver_type == 'local': |
900 msg = _("Detected 'local' zvol driver volume type " |
907 msg = _("Detected 'local' zvol driver volume type " |
901 "from volume service, which should not be " |
908 "from volume service, which should not be " |
1125 if hostname_needed and name is not None: |
1132 if hostname_needed and name is not None: |
1126 fp = os.path.join(sc_dir, 'hostname.xml') |
1133 fp = os.path.join(sc_dir, 'hostname.xml') |
1127 sysconfig.create_sc_profile(fp, sysconfig.create_hostname(name)) |
1134 sysconfig.create_sc_profile(fp, sysconfig.create_hostname(name)) |
1128 |
1135 |
1129 def _create_config(self, context, instance, network_info, |
1136 def _create_config(self, context, instance, network_info, |
1130 connection_info, extra_specs, sc_dir): |
1137 connection_info, sc_dir): |
1131 """Create a new Solaris Zone configuration.""" |
1138 """Create a new Solaris Zone configuration.""" |
1132 name = instance['name'] |
1139 name = instance['name'] |
1133 if self._get_zone_by_name(name) is not None: |
1140 if self._get_zone_by_name(name) is not None: |
1134 raise exception.InstanceExists(name=name) |
1141 raise exception.InstanceExists(name=name) |
|
1142 |
|
1143 extra_specs = self._get_extra_specs(instance) |
1135 |
1144 |
1136 # If unspecified, default zone brand is ZONE_BRAND_SOLARIS |
1145 # If unspecified, default zone brand is ZONE_BRAND_SOLARIS |
1137 brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS) |
1146 brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS) |
1138 template = ZONE_BRAND_TEMPLATE.get(brand) |
1147 template = ZONE_BRAND_TEMPLATE.get(brand) |
1139 # TODO(dcomay): Detect capability via libv12n(3LIB) or virtinfo(1M). |
1148 # TODO(dcomay): Detect capability via libv12n(3LIB) or virtinfo(1M). |
1333 console_fmri) |
1342 console_fmri) |
1334 return True |
1343 return True |
1335 except processutils.ProcessExecutionError as err: |
1344 except processutils.ProcessExecutionError as err: |
1336 return False |
1345 return False |
1337 |
1346 |
1338 def _install(self, instance, image, extra_specs, sc_dir): |
1347 def _install(self, instance, image, sc_dir): |
1339 """Install a new Solaris Zone root file system.""" |
1348 """Install a new Solaris Zone root file system.""" |
1340 name = instance['name'] |
1349 name = instance['name'] |
1341 zone = self._get_zone_by_name(name) |
1350 zone = self._get_zone_by_name(name) |
1342 if zone is None: |
1351 if zone is None: |
1343 raise exception.InstanceNotFound(instance_id=name) |
1352 raise exception.InstanceNotFound(instance_id=name) |
1438 :param network_info: |
1447 :param network_info: |
1439 :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` |
1448 :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` |
1440 :param block_device_info: Information about block devices to be |
1449 :param block_device_info: Information about block devices to be |
1441 attached to the instance. |
1450 attached to the instance. |
1442 """ |
1451 """ |
1443 inst_type = flavor_obj.Flavor.get_by_id( |
|
1444 nova_context.get_admin_context(read_deleted='yes'), |
|
1445 instance['instance_type_id']) |
|
1446 extra_specs = inst_type['extra_specs'].copy() |
|
1447 |
|
1448 image = self._fetch_image(context, instance) |
1452 image = self._fetch_image(context, instance) |
1449 self._validate_image(image, instance) |
1453 self._validate_image(image, instance) |
1450 |
1454 |
1451 # create a new directory for SC profiles |
1455 # create a new directory for SC profiles |
1452 sc_dir = tempfile.mkdtemp(prefix="nova-sysconfig-", |
1456 sc_dir = tempfile.mkdtemp(prefix="nova-sysconfig-", |
1459 # c1d0 is the standard dev for for default boot device. |
1463 # c1d0 is the standard dev for for default boot device. |
1460 # Irrelevant value for ZFS, but Cinder gets stroppy without it. |
1464 # Irrelevant value for ZFS, but Cinder gets stroppy without it. |
1461 mountpoint = "c1d0" |
1465 mountpoint = "c1d0" |
1462 try: |
1466 try: |
1463 connection_info = self._connect_boot_volume(volume, mountpoint, |
1467 connection_info = self._connect_boot_volume(volume, mountpoint, |
1464 context, instance, |
1468 context, instance) |
1465 extra_specs) |
|
1466 except exception.InvalidVolume as badvol: |
1469 except exception.InvalidVolume as badvol: |
1467 # This Cinder volume is not usable for ZOSS so discard it. |
1470 # This Cinder volume is not usable for ZOSS so discard it. |
1468 # zonecfg will apply default zonepath dataset configuration |
1471 # zonecfg will apply default zonepath dataset configuration |
1469 # instead. Carry on |
1472 # instead. Carry on |
1470 LOG.warning(_("Volume '%s' is being discarded: %s") |
1473 LOG.warning(_("Volume '%s' is being discarded: %s") |
1481 name = instance['name'] |
1484 name = instance['name'] |
1482 |
1485 |
1483 LOG.debug(_("creating zone configuration for '%s' (%s)") % |
1486 LOG.debug(_("creating zone configuration for '%s' (%s)") % |
1484 (name, instance['display_name'])) |
1487 (name, instance['display_name'])) |
1485 self._create_config(context, instance, network_info, |
1488 self._create_config(context, instance, network_info, |
1486 connection_info, extra_specs, sc_dir) |
1489 connection_info, sc_dir) |
1487 try: |
1490 try: |
1488 self._install(instance, image, extra_specs, sc_dir) |
1491 self._install(instance, image, sc_dir) |
1489 self._power_on(instance) |
1492 self._power_on(instance) |
1490 except Exception as reason: |
1493 except Exception as reason: |
1491 LOG.error(_("Unable to spawn instance '%s' via zonemgr(3RAD): %s") |
1494 LOG.error(_("Unable to spawn instance '%s' via zonemgr(3RAD): %s") |
1492 % (name, reason)) |
1495 % (name, reason)) |
1493 self._uninstall(instance) |
1496 self._uninstall(instance) |
1840 |
1843 |
1841 def attach_volume(self, context, connection_info, instance, mountpoint, |
1844 def attach_volume(self, context, connection_info, instance, mountpoint, |
1842 disk_bus=None, device_type=None, encryption=None): |
1845 disk_bus=None, device_type=None, encryption=None): |
1843 """Attach the disk to the instance at mountpoint using info.""" |
1846 """Attach the disk to the instance at mountpoint using info.""" |
1844 # TODO(npower): Apply mountpoint in a meaningful way to the zone |
1847 # TODO(npower): Apply mountpoint in a meaningful way to the zone |
1845 # (I don't think this is even possible for Solaris brand zones) |
1848 # For security reasons this is not permitted in a Solaris branded zone. |
1846 name = instance['name'] |
1849 name = instance['name'] |
1847 zone = self._get_zone_by_name(name) |
1850 zone = self._get_zone_by_name(name) |
1848 if zone is None: |
1851 if zone is None: |
1849 raise exception.InstanceNotFound(instance_id=name) |
1852 raise exception.InstanceNotFound(instance_id=name) |
1850 |
1853 |
1851 zprop = lookup_resource_property_value(zone, "global", "brand", |
1854 extra_specs = self._get_extra_specs(instance) |
1852 ZONE_BRAND_SOLARIS_KZ) |
1855 brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS) |
1853 if not zprop: |
1856 if brand != ZONE_BRAND_SOLARIS_KZ: |
1854 # Only Solaris Kernel zones are currently supported. |
1857 # Only Solaris kernel zones are currently supported. |
1855 raise NotImplementedError() |
1858 reason = (_("'%s' branded zones are not currently supported") |
|
1859 % brand) |
|
1860 raise NotImplementedError(reason) |
1856 |
1861 |
1857 suri = self._suri_from_volume_info(connection_info) |
1862 suri = self._suri_from_volume_info(connection_info) |
1858 |
1863 |
1859 with ZoneConfig(zone) as zc: |
1864 with ZoneConfig(zone) as zc: |
1860 zc.addresource("device", [zonemgr.Property("storage", suri)]) |
1865 zc.addresource("device", [zonemgr.Property("storage", suri)]) |
1865 name = instance['name'] |
1870 name = instance['name'] |
1866 zone = self._get_zone_by_name(name) |
1871 zone = self._get_zone_by_name(name) |
1867 if zone is None: |
1872 if zone is None: |
1868 raise exception.InstanceNotFound(instance_id=name) |
1873 raise exception.InstanceNotFound(instance_id=name) |
1869 |
1874 |
1870 zprop = lookup_resource_property_value(zone, "global", "brand", |
1875 extra_specs = self._get_extra_specs(instance) |
1871 ZONE_BRAND_SOLARIS_KZ) |
1876 brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS) |
1872 if not zprop: |
1877 if brand != ZONE_BRAND_SOLARIS_KZ: |
1873 # Only Solaris Kernel zones are currently supported. |
1878 # Only Solaris kernel zones are currently supported. |
1874 raise NotImplementedError() |
1879 reason = (_("'%s' branded zones are not currently supported") |
|
1880 % brand) |
|
1881 raise NotImplementedError(reason) |
1875 |
1882 |
1876 suri = self._suri_from_volume_info(connection_info) |
1883 suri = self._suri_from_volume_info(connection_info) |
1877 |
1884 |
1878 # Check if the specific property value exists before attempting removal |
1885 # Check if the specific property value exists before attempting removal |
1879 prop = lookup_resource_property_value(zone, "device", "storage", suri) |
1886 prop = lookup_resource_property_value(zone, "device", "storage", suri) |