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