|
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
|