95 lines
3.1 KiB
Python
95 lines
3.1 KiB
Python
import logging
|
|
from enum import Enum
|
|
from functools import lru_cache
|
|
from typing import Dict, Optional, Tuple, Type # noqa: F401
|
|
|
|
from uaclient import clouds, exceptions, messages, system, util
|
|
from uaclient.config import apply_config_settings_override
|
|
|
|
LOG = logging.getLogger(util.replace_top_level_logger_name(__name__))
|
|
|
|
CLOUD_TYPE_TO_TITLE = {
|
|
"aws": "AWS",
|
|
"aws-china": "AWS China",
|
|
"aws-gov": "AWS Gov",
|
|
"azure": "Azure",
|
|
"gce": "GCP",
|
|
}
|
|
|
|
PRO_CLOUD_URLS = {
|
|
"aws": messages.urls.PRO_ON_AWS_HOME_PAGE,
|
|
"azure": messages.urls.PRO_ON_AZURE_HOME_PAGE,
|
|
"gce": messages.urls.PRO_ON_GCP_HOME_PAGE,
|
|
}
|
|
|
|
|
|
class NoCloudTypeReason(Enum):
|
|
NO_CLOUD_DETECTED = 0
|
|
CLOUD_ID_ERROR = 1
|
|
|
|
|
|
def get_instance_id() -> Optional[str]:
|
|
"""Query cloud instance-id from cmdline."""
|
|
try:
|
|
# Present in cloud-init on >= Xenial
|
|
out, _err = system.subp(["cloud-init", "query", "instance_id"])
|
|
return out.strip()
|
|
except exceptions.ProcessExecutionError:
|
|
pass
|
|
LOG.warning("Unable to determine current instance-id")
|
|
return None
|
|
|
|
|
|
@lru_cache(maxsize=None)
|
|
@apply_config_settings_override("cloud_type")
|
|
def get_cloud_type() -> Tuple[Optional[str], Optional[NoCloudTypeReason]]:
|
|
if system.which("cloud-id"):
|
|
# Present in cloud-init on >= Xenial
|
|
try:
|
|
out, _err = system.subp(["cloud-id"])
|
|
return (out.strip(), None)
|
|
except exceptions.ProcessExecutionError as exc:
|
|
LOG.debug("error running cloud-id: %s", str(exc))
|
|
return (None, NoCloudTypeReason.CLOUD_ID_ERROR)
|
|
# If no cloud-id command, assume not on cloud
|
|
return (None, NoCloudTypeReason.NO_CLOUD_DETECTED)
|
|
|
|
|
|
def cloud_instance_factory(
|
|
cloud_override: Optional[str] = None,
|
|
) -> clouds.AutoAttachInstance:
|
|
"""
|
|
:raises CloudFactoryError: if no cloud instance object can be constructed
|
|
:raises CloudFactoryNoCloudError: if no cloud instance object can be
|
|
constructed because we are not on a cloud
|
|
:raises CloudFactoryUnsupportedCloudError: if no cloud instance object can
|
|
be constructed because we don't have a class for the cloud we're on
|
|
:raises CloudFactoryNonViableCloudError: if no cloud instance object can be
|
|
constructed because we explicitly do not support the cloud we're on
|
|
"""
|
|
from uaclient.clouds import aws, azure, gcp, lxd
|
|
|
|
cloud_instance_map = {
|
|
"aws": aws.AWSAutoAttachInstance,
|
|
"aws-china": aws.AWSAutoAttachInstance,
|
|
"aws-gov": aws.AWSAutoAttachInstance,
|
|
"azure": azure.AzureAutoAttachInstance,
|
|
"gce": gcp.GCPAutoAttachInstance,
|
|
"lxd": lxd.LXDAutoAttachInstance,
|
|
} # type: Dict[str, Type[clouds.AutoAttachInstance]]
|
|
|
|
if cloud_override is not None:
|
|
cloud_type = cloud_override
|
|
else:
|
|
cloud_type, _ = get_cloud_type()
|
|
|
|
if not cloud_type:
|
|
raise exceptions.CloudFactoryNoCloudError()
|
|
cls = cloud_instance_map.get(cloud_type)
|
|
if not cls:
|
|
raise exceptions.NonAutoAttachImageError(cloud_type=cloud_type)
|
|
instance = cls()
|
|
if not instance.is_viable:
|
|
raise exceptions.CloudFactoryNonViableCloudError()
|
|
return instance
|