Blame tests/cloud_tests/platforms/azurecloud/instance.py

Packit Service a04d08
# This file is part of cloud-init. See LICENSE file for license information.
Packit Service a04d08
Packit Service a04d08
"""Base Azure Cloud instance."""
Packit Service a04d08
Packit Service a04d08
from datetime import datetime, timedelta
Packit Service a04d08
from urllib.parse import urlparse
Packit Service a04d08
from time import sleep
Packit Service a04d08
import traceback
Packit Service a04d08
import os
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
# pylint: disable=no-name-in-module
Packit Service a04d08
from azure.storage.blob import BlockBlobService, BlobPermissions
Packit Service a04d08
from msrestazure.azure_exceptions import CloudError
Packit Service a04d08
Packit Service a04d08
from tests.cloud_tests import LOG
Packit Service a04d08
Packit Service a04d08
from ..instances import Instance
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class AzureCloudInstance(Instance):
Packit Service a04d08
    """Azure Cloud backed instance."""
Packit Service a04d08
Packit Service a04d08
    platform_name = 'azurecloud'
Packit Service a04d08
Packit Service a04d08
    def __init__(self, platform, properties, config,
Packit Service a04d08
                 features, image_id, user_data=None):
Packit Service a04d08
        """Set up instance.
Packit Service a04d08
Packit Service a04d08
        @param platform: platform object
Packit Service a04d08
        @param properties: dictionary of properties
Packit Service a04d08
        @param config: dictionary of configuration values
Packit Service a04d08
        @param features: dictionary of supported feature flags
Packit Service a04d08
        @param image_id: image to find and/or use
Packit Service a04d08
        @param user_data: test user-data to pass to instance
Packit Service a04d08
        """
Packit Service a04d08
        super(AzureCloudInstance, self).__init__(
Packit Service a04d08
            platform, image_id, properties, config, features)
Packit Service a04d08
Packit Service a04d08
        self.ssh_port = 22
Packit Service a04d08
        self.ssh_ip = None
Packit Service a04d08
        self.instance = None
Packit Service a04d08
        self.image_id = image_id
Packit Service 751c4a
        self.vm_name = 'ci-azure-i-%s' % self.platform.tag
Packit Service a04d08
        self.user_data = user_data
Packit Service a04d08
        self.ssh_key_file = os.path.join(
Packit Service a04d08
            platform.config['data_dir'], platform.config['private_key'])
Packit Service a04d08
        self.ssh_pubkey_file = os.path.join(
Packit Service a04d08
            platform.config['data_dir'], platform.config['public_key'])
Packit Service a04d08
        self.blob_client, self.container, self.blob = None, None, None
Packit Service a04d08
Packit Service a04d08
    def start(self, wait=True, wait_for_cloud_init=False):
Packit Service a04d08
        """Start instance with the platforms NIC."""
Packit Service a04d08
        if self.instance:
Packit Service a04d08
            return
Packit Service a04d08
        data = self.image_id.split('-')
Packit Service a04d08
        release, support = data[2].replace('_', '.'), data[3]
Packit Service a04d08
        sku = '%s-%s' % (release, support) if support == 'LTS' else release
Packit Service a04d08
        image_resource_id = '/subscriptions/%s' \
Packit Service a04d08
                            '/resourceGroups/%s' \
Packit Service a04d08
                            '/providers/Microsoft.Compute/images/%s' % (
Packit Service a04d08
                                self.platform.subscription_id,
Packit Service a04d08
                                self.platform.resource_group.name,
Packit Service a04d08
                                self.image_id)
Packit Service a04d08
        storage_uri = "http://%s.blob.core.windows.net" \
Packit Service a04d08
                      % self.platform.storage.name
Packit Service a04d08
        with open(self.ssh_pubkey_file, 'r') as key:
Packit Service a04d08
            ssh_pub_keydata = key.read()
Packit Service a04d08
Packit Service a04d08
        image_exists = False
Packit Service a04d08
        try:
Packit Service a04d08
            LOG.debug('finding image in resource group using image_id')
Packit Service a04d08
            self.platform.compute_client.images.get(
Packit Service a04d08
                self.platform.resource_group.name,
Packit Service a04d08
                self.image_id
Packit Service a04d08
            )
Packit Service a04d08
            image_exists = True
Packit Service 751c4a
            LOG.debug('image found, launching instance, image_id=%s',
Packit Service 751c4a
                      self.image_id)
Packit Service a04d08
        except CloudError:
Packit Service 751c4a
            LOG.debug(('image not found, launching instance with base image, '
Packit Service 751c4a
                       'image_id=%s'), self.image_id)
Packit Service a04d08
Packit Service a04d08
        vm_params = {
Packit Service 751c4a
            'name': self.vm_name,
Packit Service a04d08
            'location': self.platform.location,
Packit Service a04d08
            'os_profile': {
Packit Service 751c4a
                'computer_name': 'CI-%s' % self.platform.tag,
Packit Service a04d08
                'admin_username': self.ssh_username,
Packit Service a04d08
                "customData": self.user_data,
Packit Service a04d08
                "linuxConfiguration": {
Packit Service a04d08
                    "disable_password_authentication": True,
Packit Service a04d08
                    "ssh": {
Packit Service a04d08
                        "public_keys": [{
Packit Service a04d08
                            "path": "/home/%s/.ssh/authorized_keys" %
Packit Service a04d08
                                    self.ssh_username,
Packit Service a04d08
                            "keyData": ssh_pub_keydata
Packit Service a04d08
                        }]
Packit Service a04d08
                    }
Packit Service a04d08
                }
Packit Service a04d08
            },
Packit Service a04d08
            "diagnosticsProfile": {
Packit Service a04d08
                "bootDiagnostics": {
Packit Service a04d08
                    "storageUri": storage_uri,
Packit Service a04d08
                    "enabled": True
Packit Service a04d08
                }
Packit Service a04d08
            },
Packit Service a04d08
            'hardware_profile': {
Packit Service a04d08
                'vm_size': self.platform.vm_size
Packit Service a04d08
            },
Packit Service a04d08
            'storage_profile': {
Packit Service a04d08
                'image_reference': {
Packit Service a04d08
                    'id': image_resource_id
Packit Service a04d08
                } if image_exists else {
Packit Service a04d08
                    'publisher': 'Canonical',
Packit Service a04d08
                    'offer': 'UbuntuServer',
Packit Service a04d08
                    'sku': sku,
Packit Service a04d08
                    'version': 'latest'
Packit Service a04d08
                }
Packit Service a04d08
            },
Packit Service a04d08
            'network_profile': {
Packit Service a04d08
                'network_interfaces': [{
Packit Service a04d08
                    'id': self.platform.nic.id
Packit Service a04d08
                }]
Packit Service a04d08
            },
Packit Service a04d08
            'tags': {
Packit Service a04d08
                'Name': self.platform.tag,
Packit Service a04d08
            }
Packit Service a04d08
        }
Packit Service a04d08
Packit Service a04d08
        try:
Packit Service a04d08
            self.instance = self.platform.compute_client.virtual_machines.\
Packit Service a04d08
                create_or_update(self.platform.resource_group.name,
Packit Service 751c4a
                                 self.vm_name, vm_params)
Packit Service 751c4a
            LOG.debug('creating instance %s from image_id=%s', self.vm_name,
Packit Service 751c4a
                      self.image_id)
Packit Service 751c4a
        except CloudError as e:
Packit Service 751c4a
            raise RuntimeError(
Packit Service 751c4a
                'failed creating instance:\n{}'.format(traceback.format_exc())
Packit Service 751c4a
            ) from e
Packit Service a04d08
Packit Service a04d08
        if wait:
Packit Service a04d08
            self.instance.wait()
Packit Service a04d08
            self.ssh_ip = self.platform.network_client.\
Packit Service a04d08
                public_ip_addresses.get(
Packit Service a04d08
                    self.platform.resource_group.name,
Packit Service a04d08
                    self.platform.public_ip.name
Packit Service a04d08
                ).ip_address
Packit Service a04d08
            self._wait_for_system(wait_for_cloud_init)
Packit Service a04d08
Packit Service a04d08
        self.instance = self.instance.result()
Packit Service a04d08
        self.blob_client, self.container, self.blob =\
Packit Service a04d08
            self._get_blob_client()
Packit Service a04d08
Packit Service a04d08
    def shutdown(self, wait=True):
Packit Service a04d08
        """Finds console log then stopping/deallocates VM"""
Packit Service a04d08
        LOG.debug('waiting on console log before stopping')
Packit Service a04d08
        attempts, exists = 5, False
Packit Service a04d08
        while not exists and attempts:
Packit Service a04d08
            try:
Packit Service a04d08
                attempts -= 1
Packit Service a04d08
                exists = self.blob_client.get_blob_to_bytes(
Packit Service a04d08
                    self.container, self.blob)
Packit Service a04d08
                LOG.debug('found console log')
Packit Service a04d08
            except Exception as e:
Packit Service a04d08
                if attempts:
Packit Service a04d08
                    LOG.debug('Unable to find console log, '
Packit Service a04d08
                              '%s attempts remaining', attempts)
Packit Service a04d08
                    sleep(15)
Packit Service a04d08
                else:
Packit Service a04d08
                    LOG.warning('Could not find console log: %s', e)
Packit Service a04d08
Packit Service a04d08
        LOG.debug('stopping instance %s', self.image_id)
Packit Service a04d08
        vm_deallocate = \
Packit Service a04d08
            self.platform.compute_client.virtual_machines.deallocate(
Packit Service a04d08
                self.platform.resource_group.name, self.image_id)
Packit Service a04d08
        if wait:
Packit Service a04d08
            vm_deallocate.wait()
Packit Service a04d08
Packit Service a04d08
    def destroy(self):
Packit Service a04d08
        """Delete VM and close all connections"""
Packit Service a04d08
        if self.instance:
Packit Service a04d08
            LOG.debug('destroying instance: %s', self.image_id)
Packit Service a04d08
            vm_delete = self.platform.compute_client.virtual_machines.delete(
Packit Service a04d08
                self.platform.resource_group.name, self.image_id)
Packit Service a04d08
            vm_delete.wait()
Packit Service a04d08
Packit Service a04d08
        self._ssh_close()
Packit Service a04d08
Packit Service a04d08
        super(AzureCloudInstance, self).destroy()
Packit Service a04d08
Packit Service a04d08
    def _execute(self, command, stdin=None, env=None):
Packit Service a04d08
        """Execute command on instance."""
Packit Service a04d08
        env_args = []
Packit Service a04d08
        if env:
Packit Service a04d08
            env_args = ['env'] + ["%s=%s" for k, v in env.items()]
Packit Service a04d08
Packit Service a04d08
        return self._ssh(['sudo'] + env_args + list(command), stdin=stdin)
Packit Service a04d08
Packit Service a04d08
    def _get_blob_client(self):
Packit Service a04d08
        """
Packit Service a04d08
        Use VM details to retrieve container and blob name.
Packit Service a04d08
        Then Create blob service client for sas token to
Packit Service a04d08
        retrieve console log.
Packit Service a04d08
Packit Service a04d08
        :return: blob service, container name, blob name
Packit Service a04d08
        """
Packit Service a04d08
        LOG.debug('creating blob service for console log')
Packit Service a04d08
        storage = self.platform.storage_client.storage_accounts.get_properties(
Packit Service a04d08
            self.platform.resource_group.name, self.platform.storage.name)
Packit Service a04d08
Packit Service a04d08
        keys = self.platform.storage_client.storage_accounts.list_keys(
Packit Service a04d08
            self.platform.resource_group.name, self.platform.storage.name
Packit Service a04d08
        ).keys[0].value
Packit Service a04d08
Packit Service a04d08
        virtual_machine = self.platform.compute_client.virtual_machines.get(
Packit Service a04d08
            self.platform.resource_group.name, self.instance.name,
Packit Service a04d08
            expand='instanceView')
Packit Service a04d08
Packit Service a04d08
        blob_uri = virtual_machine.instance_view.boot_diagnostics.\
Packit Service a04d08
            serial_console_log_blob_uri
Packit Service a04d08
Packit Service a04d08
        container, blob = urlparse(blob_uri).path.split('/')[-2:]
Packit Service a04d08
Packit Service a04d08
        blob_client = BlockBlobService(
Packit Service a04d08
            account_name=storage.name,
Packit Service a04d08
            account_key=keys)
Packit Service a04d08
Packit Service a04d08
        sas = blob_client.generate_blob_shared_access_signature(
Packit Service a04d08
            container_name=container, blob_name=blob, protocol='https',
Packit Service a04d08
            expiry=datetime.utcnow() + timedelta(hours=1),
Packit Service a04d08
            permission=BlobPermissions.READ)
Packit Service a04d08
Packit Service a04d08
        blob_client = BlockBlobService(
Packit Service a04d08
            account_name=storage.name,
Packit Service a04d08
            sas_token=sas)
Packit Service a04d08
Packit Service a04d08
        return blob_client, container, blob
Packit Service a04d08
Packit Service a04d08
    def console_log(self):
Packit Service a04d08
        """Instance console.
Packit Service a04d08
Packit Service a04d08
        @return_value: bytes of this instance’s console
Packit Service a04d08
        """
Packit Service a04d08
        boot_diagnostics = self.blob_client.get_blob_to_bytes(
Packit Service a04d08
            self.container, self.blob)
Packit Service a04d08
        return boot_diagnostics.content