25 import platform |
25 import platform |
26 import shutil |
26 import shutil |
27 import tempfile |
27 import tempfile |
28 import uuid |
28 import uuid |
29 |
29 |
|
30 import rad.bindings.com.oracle.solaris.rad.archivemgr_1 as archivemgr |
30 import rad.bindings.com.oracle.solaris.rad.kstat_1 as kstat |
31 import rad.bindings.com.oracle.solaris.rad.kstat_1 as kstat |
31 import rad.bindings.com.oracle.solaris.rad.zonemgr_1 as zonemgr |
32 import rad.bindings.com.oracle.solaris.rad.zonemgr_1 as zonemgr |
32 import rad.client |
33 import rad.client |
33 import rad.connect |
34 import rad.connect |
34 from solaris_install.archive.checkpoints import InstantiateUnifiedArchive |
|
35 from solaris_install.archive import LOGFILE as ARCHIVE_LOGFILE |
|
36 from solaris_install.archive import UnifiedArchive |
|
37 from solaris_install.engine import InstallEngine |
|
38 from solaris_install.target.size import Size |
35 from solaris_install.target.size import Size |
39 |
36 |
40 from cinderclient import exceptions as cinder_exception |
37 from cinderclient import exceptions as cinder_exception |
41 from eventlet import greenthread |
38 from eventlet import greenthread |
42 from lxml import etree |
39 from lxml import etree |
479 self._rad_connection = rad.connect.connect_unix() |
476 self._rad_connection = rad.connect.connect_unix() |
480 |
477 |
481 return self._rad_connection |
478 return self._rad_connection |
482 |
479 |
483 def _init_rad(self): |
480 def _init_rad(self): |
484 """Connect to RAD providers for kernel statistics and Solaris |
481 """Obtain required RAD objects for Solaris Zones, kernel statistics, |
485 Zones. By connecting to the local rad(1M) service through a |
482 and Unified Archive management. |
486 UNIX domain socket, kernel statistics can be read via |
483 """ |
487 kstat(3RAD) and Solaris Zones can be configured and controlled |
484 try: |
488 via zonemgr(3RAD). |
485 self._zone_manager = self.rad_connection.get_object( |
489 """ |
486 zonemgr.ZoneManager()) |
490 |
|
491 try: |
|
492 self._kstat_control = self.rad_connection.get_object( |
487 self._kstat_control = self.rad_connection.get_object( |
493 kstat.Control()) |
488 kstat.Control()) |
494 except Exception as reason: |
489 self._archive_manager = self.rad_connection.get_object( |
495 msg = (_('Unable to connect to svc:/system/rad:local: %s') |
490 archivemgr.ArchiveManager()) |
496 % reason) |
491 except Exception as ex: |
497 raise exception.NovaException(msg) |
492 reason = _("Unable to obtain RAD object: %s") % ex |
|
493 raise exception.NovaException(reason) |
498 |
494 |
499 def init_host(self, host): |
495 def init_host(self, host): |
500 """Initialize anything that is necessary for the driver to function, |
496 """Initialize anything that is necessary for the driver to function, |
501 including catching up with currently running VM's on the given host. |
497 including catching up with currently running VM's on the given host. |
502 """ |
498 """ |
863 % (instance['image_ref'], reason)) |
859 % (instance['image_ref'], reason)) |
864 raise |
860 raise |
865 return image |
861 return image |
866 |
862 |
867 def _validate_image(self, image, instance): |
863 def _validate_image(self, image, instance): |
868 """Validate a glance image for compatibility with the instance""" |
864 """Validate a glance image for compatibility with the instance.""" |
869 # Skip if the image was already checked and confirmed as valid |
865 # Skip if the image was already checked and confirmed as valid. |
870 if instance['image_ref'] in self._validated_archives: |
866 if instance['image_ref'] in self._validated_archives: |
871 return |
867 return |
872 |
868 |
873 if self._install_engine is None: |
869 try: |
874 self._install_engine = InstallEngine(ARCHIVE_LOGFILE) |
870 ua = self._archive_manager.getArchive(image) |
875 |
871 except Exception as ex: |
876 try: |
872 reason = ex.get_payload().info |
877 init_ua_cp = InstantiateUnifiedArchive(instance['image_ref'], |
|
878 image) |
|
879 init_ua_cp.execute() |
|
880 except Exception: |
|
881 reason = (_("Image query failed. Possibly invalid or corrupt. " |
|
882 "Log file location: %s:%s") |
|
883 % (self._uname[1], ARCHIVE_LOGFILE)) |
|
884 LOG.error(reason) |
|
885 raise exception.ImageUnacceptable(image_id=instance['image_ref'], |
|
886 reason=reason) |
|
887 |
|
888 try: |
|
889 ua = self._install_engine.doc.volatile.get_first_child( |
|
890 class_type=UnifiedArchive) |
|
891 # Validate the image at this point to ensure: |
|
892 # - contains one deployable system |
|
893 deployables = ua.archive_objects |
|
894 if len(deployables) != 1: |
|
895 reason = (_('Image must contain only 1 deployable system')) |
|
896 raise exception.ImageUnacceptable( |
873 raise exception.ImageUnacceptable( |
897 image_id=instance['image_ref'], |
874 image_id=instance['image_ref'], |
898 reason=reason) |
875 reason=reason) |
899 # - matching architecture |
876 |
900 deployable_arch = deployables[0].system.arch |
877 # Validate the image at this point to ensure: |
901 compute_arch = platform.processor() |
878 # - contains one deployable system |
902 if deployable_arch != compute_arch: |
879 deployables = ua.getArchivedSystems() |
903 reason = (_('Image architecture "%s" is incompatible with this' |
880 if len(deployables) != 1: |
904 'compute host architecture: "%s"') |
881 reason = _("Image must contain only a single deployable system.") |
905 % (deployable_arch, compute_arch)) |
882 raise exception.ImageUnacceptable(image_id=instance['image_ref'], |
906 raise exception.ImageUnacceptable( |
883 reason=reason) |
907 image_id=instance['image_ref'], |
884 # - matching architecture |
908 reason=reason) |
885 deployable_arch = str(ua.isa) |
909 # - single root pool only |
886 compute_arch = platform.processor() |
910 streams = deployables[0].zfs_streams |
887 if deployable_arch.lower() != compute_arch: |
911 stream_pools = set(stream.zpool for stream in streams) |
888 reason = (_("Unified Archive architecture '%s' is incompatible " |
912 if len(stream_pools) > 1: |
889 "with this compute host's architecture, '%s'.") |
913 reason = (_('Image contains more than one zpool: "%s"') |
890 % (deployable_arch, compute_arch)) |
914 % (stream_pools)) |
891 raise exception.ImageUnacceptable(image_id=instance['image_ref'], |
915 raise exception.ImageUnacceptable( |
892 reason=reason) |
916 image_id=instance['image_ref'], |
893 # - single root pool only |
917 reason=reason) |
894 if not deployables[0].rootOnly: |
918 # - looks like it's OK |
895 reason = _("Image contains more than one ZFS pool.") |
919 self._validated_archives.append(instance['image_ref']) |
896 raise exception.ImageUnacceptable(image_id=instance['image_ref'], |
920 finally: |
897 reason=reason) |
921 # Clear the reference to the UnifiedArchive object in the engine |
898 # - looks like it's OK |
922 # data cache to avoid collision with the next checkpoint execution. |
899 self._validated_archives.append(instance['image_ref']) |
923 self._install_engine.doc.volatile.delete_children( |
|
924 class_type=UnifiedArchive) |
|
925 |
900 |
926 def _suri_from_volume_info(self, connection_info): |
901 def _suri_from_volume_info(self, connection_info): |
927 """Returns a suri(5) formatted string based on connection_info |
902 """Returns a suri(5) formatted string based on connection_info |
928 Currently supports local ZFS volume and iSCSI driver types. |
903 Currently supports local ZFS volume and iSCSI driver types. |
929 """ |
904 """ |
1354 |
1329 |
1355 self._verify_sysconfig(sc_dir, instance, admin_password) |
1330 self._verify_sysconfig(sc_dir, instance, admin_password) |
1356 |
1331 |
1357 LOG.debug(_("Creating zone configuration for '%s' (%s)") |
1332 LOG.debug(_("Creating zone configuration for '%s' (%s)") |
1358 % (name, instance['display_name'])) |
1333 % (name, instance['display_name'])) |
1359 zonemanager = self.rad_connection.get_object(zonemgr.ZoneManager()) |
1334 try: |
1360 try: |
1335 self._zone_manager.create(name, None, template) |
1361 zonemanager.create(name, None, template) |
|
1362 self._set_global_properties(name, extra_specs, brand) |
1336 self._set_global_properties(name, extra_specs, brand) |
1363 if connection_info is not None: |
1337 if connection_info is not None: |
1364 self._set_boot_device(name, connection_info, brand) |
1338 self._set_boot_device(name, connection_info, brand) |
1365 self._set_num_cpu(name, instance['vcpus'], brand) |
1339 self._set_num_cpu(name, instance['vcpus'], brand) |
1366 self._set_memory_cap(name, instance['memory_mb'], brand) |
1340 self._set_memory_cap(name, instance['memory_mb'], brand) |
1618 """Delete an existing Solaris Zone configuration.""" |
1592 """Delete an existing Solaris Zone configuration.""" |
1619 name = instance['name'] |
1593 name = instance['name'] |
1620 if self._get_zone_by_name(name) is None: |
1594 if self._get_zone_by_name(name) is None: |
1621 raise exception.InstanceNotFound(instance_id=name) |
1595 raise exception.InstanceNotFound(instance_id=name) |
1622 |
1596 |
1623 zonemanager = self.rad_connection.get_object(zonemgr.ZoneManager()) |
1597 try: |
1624 try: |
1598 self._zone_manager.delete(name) |
1625 zonemanager.delete(name) |
|
1626 except Exception as ex: |
1599 except Exception as ex: |
1627 reason = zonemgr_strerror(ex) |
1600 reason = zonemgr_strerror(ex) |
1628 LOG.error(_("Unable to delete configuration for instance '%s' via " |
1601 LOG.error(_("Unable to delete configuration for instance '%s' via " |
1629 "zonemgr(3RAD): %s") % (name, reason)) |
1602 "zonemgr(3RAD): %s") % (name, reason)) |
1630 raise |
1603 raise |