from __future__ import print_function
from azure.servicemanagement import *
from pprint import pprint
from cloudmesh_client.common.ConfigDict import ConfigDict
from cloudmesh_client.common.util import generate_password
from cloudmesh_client.cloud.iaas.provider.azure.AzureDict import AzureDict
from cloudmesh_client.shell.console import Console
from cloudmesh_client.cloud.iaas.CloudProviderBase import CloudProviderBase
import base64
import sys
import traceback
[docs]class CloudProviderAzureAPI(CloudProviderBase):
def __init__(self, cloud_name, cloud_details, user=None):
super(CloudProviderAzureAPI, self).__init__(cloud_name, user=user)
self.cloud_type = "azure"
self.kind = ["image", "flavor", "vm"]
self.provider = None
self.default_image = None
self.default_flavor = None
self.cloud = None
self.cloud_details = None
self.initialize(cloud_name, cloud_details)
[docs] def initialize(self, cloudname, user=None):
"""
reads the details for the initialization from the cloudname defined in the yaml file
Azure cloud requires subscription_id and service management certificate to initialize the provider
:param cloudname:
:param user:
:return:
"""
confd = ConfigDict("cloudmesh.yaml")
cloudcred = confd['cloudmesh']['clouds']['azure']['credentials']
subscription_id = cloudcred['subscriptionid']
certificate_path = cloudcred['managementcertfile']
# DEBUGGING INFO
# pprint("subscriptionid:"+subscription_id)
# pprint("certificate_path:"+certificate_path)
self.provider = ServiceManagementService(subscription_id, certificate_path)
self.default_image = confd['cloudmesh']['clouds']['azure']['default']['image']
self.default_flavor = confd['cloudmesh']['clouds']['azure']['default']['flavor']
self.cloud = "azure"
self.cloud_details = confd['cloudmesh']['clouds']['azure']
self.location = confd["cloudmesh"]["clouds"]["azure"]["default"]["location"]
def _to_dict(self, dict_list):
final_dict = dict()
for index, result_dict in enumerate(dict_list):
# pprint("index"+str(index))
final_dict[index] = result_dict
return final_dict
[docs] def list_flavor(self, cloudname, **kwargs):
result = self.provider.list_role_sizes()
flavor_dict_list = []
for index, role_size in enumerate(result):
# print("SIZE ", index)
flavor_dict = AzureDict.convert_to_flavor_dict(role_size)
flavor_dict_list.append(flavor_dict)
return self._to_dict(flavor_dict_list)
[docs] def list_image(self, cloudname, **kwargs):
result = self.provider.list_os_images()
image_dict_list = []
for index, image in enumerate(result):
# print("IMAGE ", index)
image_dict = AzureDict.convert_to_image_dict(image)
image_dict_list.append(image_dict)
return self._to_dict(image_dict_list)
[docs] def list_vm(self, cloudname, **kwargs):
"""
List the VMs or Deployments in Azure Cloud
:param cloudname:
:param kwargs:
:return:
"""
# pprint("In list_vm for Azure")
result = self.provider.list_hosted_services()
vm_dict_list = []
for hosted_service in result:
# DEBUGGING INFO
# pprint("Detail of:" + hosted_service.service_name)
hosted_service_detail = self.provider.get_hosted_service_properties(hosted_service.service_name, embed_detail=True)
for key, deployment in enumerate(hosted_service_detail.deployments):
# print("Dict of the VM Object", key)
vm_dict = AzureDict.convert_to_vm_dict(hosted_service, deployment)
vm_dict_list.append(vm_dict)
return self._to_dict(vm_dict_list)
[docs] def list_secgroup_rules(self, cloudname):
Console.TODO("not yet implemented")
[docs] def list_secgroup(self, cloudname):
Console.TODO("not yet implemented")
[docs] def add_certificate(self, service_name, certificate_path):
"""
:param service_name: Hosted service name in Azure cloud to which the certificate needs to be added
:param certificate_path: PFX(PKCS encoded) certificate Path
:return:
"""
with open(certificate_path, "rb") as bfile:
print("Adding the PFX certificate")
cert_data = base64.b64encode(bfile.read())
cert_format = 'pfx'
# THIS PASSWORD SHOULD BE DEFINED IN cloudmesh.yml
cert_password = ''
cert_res = self.provider.add_service_certificate(service_name=service_name,
data=cert_data,
certificate_format=cert_format,
password=cert_password)
print(cert_res)
self.provider.wait_for_operation_status(cert_res.request_id, timeout=30)
def _get_storage_name(self):
"""
Fetches the default storage service name from the Azure cloud
:return:
"""
result = self.provider.list_storage_accounts()
if len(result) > 0:
Console.info("{0} storage found ".format(result[0].service_name))
return result[0].service_name
Console.warning("No Storage Accounts found")
return None
def _create_storage_account(self):
"""
Storage service is required to create a disk space for a VM
Reference: SimpleAzure
"""
# Note that it is better to create a storage account with a VM name included.
# And have a unique name to avoid conflict
# TODO: Get a unique readable storage account name
# Tips: no dash(-) or space is allowed in naming
temp_name = "cloudmesh"
name = temp_name[:24].replace("-","")
description = name + "description"
label = name + "label"
result = self.provider.create_storage_account(service_name=name,
description=description, label=label,
location=self.get_location())
operation_result = self.provider.get_operation_status(result.request_id)
Console.info("Storage Account creation: " + operation_result.status)
[docs] def get_location(self):
return self.location
[docs] def boot_vm(self,
name,
group=None,
image=None,
flavor=None,
cloud=None,
cert_thumbprint=None,
pub_key_path=None,
cert_path=None,
pfx_path=None,
secgroup=None,
meta=None,
nics=None,
**kwargs):
"""
Boots up a new VM Instance.
Steps involved: creating a hosted(Cloud) Service, adding the PFX certificate file,
get default storage name, creating a configuration set, adding an endpoint(SSH by default),
and finally creating a VM deployment
:param name: Hosted Service Name and VM instance name
:param group:
:param image:
:param flavor:
:param cloud:
:param cert_thumbprint:
:param pub_key_path:
:param cert_path:
:param pfx_path:
:param secgroup:
:param meta:
:param nics:
:param kwargs:
:return:
"""
location = ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["clouds"]["azure"]["default"]["location"] or 'Central US'
try:
self.provider.create_hosted_service(service_name=name,
label=name,
location=location)
except:
Console.error("Failed to create hosted service in Azure: {0}".format(traceback.format_exc()))
try:
Console.info("service name: " + name)
Console.info("location name: " + location)
Console.info("cert_thumbprint: " + cert_thumbprint)
Console.info("pub_key_path: " + pub_key_path)
Console.info("cert_path: " + cert_path)
Console.info("pfx_path:" + pfx_path)
Console.info("Image:" + image)
Console.info("Flavor:" + flavor)
#Console.info("Certificate adding")
# Disabled - not required to start Virtual Machine
#self.add_certificate(name, pfx_path)
#Console.info("Certificate added")
except Exception as e:
Console.warning("Console.info error: {0}".format(traceback.format_exc()))
storage_name = self._get_storage_name()
if storage_name is None:
self._create_storage_account()
storage_name = self._get_storage_name()
media_link = 'https://{0}.blob.core.windows.net/vhds/{1}.vhd'.format(
storage_name, name)
os_hd = OSVirtualHardDisk(image, media_link)
username = ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["clouds"]["azure"]["default"]["username"]
password = ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["clouds"]["azure"]["default"]["password"]
# Auto-generated Password in case of TBD
if username.lower() in ["tbd"]:
username = "azure";
if password.lower() in ["tbd"]:
password = generate_password(16)
Console.info("Username: "+username)
Console.info("password: "+password)
# TODO: current case handles only for linux guest VMs, implementation needed for Windows VMs
# SSH key configuration does not work properly - DISABLED
linux_config = LinuxConfigurationSet(name, username, password, False)
linux_config.ssh = SSH()
public_key = PublicKey(cert_thumbprint, pub_key_path)
#linux_config.ssh.public_keys.public_keys.append(public_key)
pair = KeyPair(cert_thumbprint, cert_path)
#linux_config.ssh.key_pairs.key_pairs.append(pair)
# Endpoint configuration
network = ConfigurationSet()
network.configuration_set_type = 'NetworkConfiguration'
network.input_endpoints.input_endpoints.append(
ConfigurationSetInputEndpoint('SSH', 'tcp', '22', '22'))
Console.info("blob storage location: {0} ".format(media_link))
try:
vm_create_result = self.provider.create_virtual_machine_deployment(service_name=name,
deployment_name=name,
deployment_slot='production',
label=name,
role_name=name,
system_config=linux_config,
os_virtual_hard_disk=os_hd,
network_config=network,
role_size=flavor)
# pprint(vm_create_result)
self.provider.wait_for_operation_status(vm_create_result.request_id, timeout=30)
Console.info("{0} created successfully".format(name))
except:
Console.error("Failed to start Azure Virtual Machine: {0}".format(traceback.format_exc()))
return name
[docs] def get_ips(self, name, group=None, force=None):
"""
Returns the ip of the instance indicated by name
:param name:
:param group:
:param force:
:return: IP address of the instance
"""
#pprint("In get_ips::")
result = self.provider.list_hosted_services()
for hosted_service in result:
# pprint("Detail of:" + hosted_service.service_name)
hosted_service_detail = self.provider.get_hosted_service_properties(hosted_service.service_name, embed_detail=True)
for key, deployment in enumerate(hosted_service_detail.deployments):
vm_dict = AzureDict.convert_to_vm_dict(hosted_service, deployment)
instance_name = vm_dict['instance_name']
if instance_name == name:
vip_list = []
vip_list.append(vm_dict['public_ips'])
return vip_list
return None
[docs] def delete_vm(self, name, group=None, force=None):
"""
Delete a VM instance whose hosted service name and instance name is given by name
:param name:
:param group:
:param force:
:return:
"""
self.provider.delete_deployment(service_name=name,
deployment_name=name)
# Wait a sec before deleting hosted service?
import time
time.sleep(10)
self.provider.delete_hosted_service(service_name=name)
Console.info("{0} deleted".format(name))
[docs] def assign_ip(self, name):
Console.TODO("not yet implemented")
[docs] def attributes(self, kind):
layout = {
'vm': {
'order': [
'id',
'uuid',
'label',
'status',
'public_ips',
'private_ips',
'image_name',
'key',
'availability',
'instance_type',
'user',
'category',
'updated_at'
],
'header': [
'id',
'uuid',
'label',
'status',
'public_ips',
'private_ips',
'image_name',
'key',
'availability',
'instance_type',
'user',
'cloud',
'updated'
]
},
'image': {
'order': [
'uuid',
'affinity_group',
'category',
'label',
'media_link',
'os',
'vm_image',
'image_family'
],
'header': [
'uuid',
'affinity_group',
'category',
'label',
'media_link',
'os',
'vm_image',
'image_family'
]
},
'flavor': {
'order': [
'uuid',
'label',
'web_worker_resource_disk_size',
'virtual_machine_resource_disk_size',
'ram',
'cores',
'max_data_disks'
],
'header': [
'uuid',
'label',
'web_worker_resource_disk_size',
'virtual_machine_resource_disk_size',
'ram',
'cores',
'max_data_disks'
]
}
}
if kind in layout:
order = layout[kind]['order']
header = layout[kind]['header']
else:
order = None
header = None
return order, header
#
# All other must methods defined bellow so we can discuss
#