예제 #1
0
    def delete_machine(self, instance_ids, deployment_name=None,
                       cleanup_service=None, **kwargs):
        sms = ServiceManagementService(self.profile.username,
                                       self.cert_path)
        for inst in instance_ids:
            try:
                sms.delete_deployment(service_name=inst,
                                      deployment_name=deployment_name)
                if cleanup_service:
                    sms.delete_hosted_service(service_name=inst)

            except Exception, ex:
                self.log.exception(ex)
                return self.FAIL
class AffinityGroupManagementServiceTest(AzureTestCase):

    def setUp(self):
        self.sms = ServiceManagementService(credentials.getSubscriptionId(), 
                                            credentials.getManagementCertFile())

        self.sms.set_proxy(credentials.getProxyHost(), 
                           credentials.getProxyPort(), 
                           credentials.getProxyUser(), 
                           credentials.getProxyPassword())

        self.affinity_group_name = getUniqueNameBasedOnCurrentTime('utaffgrp')
        self.hosted_service_name = None
        self.storage_account_name = None

    def tearDown(self):
        try:
            if self.hosted_service_name is not None:
                self.sms.delete_hosted_service(self.hosted_service_name)
        except: pass

        try:
            if self.storage_account_name is not None:
                self.sms.delete_storage_account(self.storage_account_name)
        except: pass

        try:
            self.sms.delete_affinity_group(self.affinity_group_name)
        except: pass

    #--Helpers-----------------------------------------------------------------
    def _create_affinity_group(self, name):
        result = self.sms.create_affinity_group(name, 'tstmgmtaffgrp', 'West US', 'tstmgmt affinity group')
        self.assertIsNone(result)

    def _affinity_group_exists(self, name):
        try:
            props = self.sms.get_affinity_group_properties(name)
            return props is not None
        except:
            return False

    #--Test cases for affinity groups ------------------------------------
    def test_list_affinity_groups(self):
        # Arrange
        self._create_affinity_group(self.affinity_group_name)

        # Act
        result = self.sms.list_affinity_groups()
        
        # Assert
        self.assertIsNotNone(result)
        self.assertTrue(len(result) > 0)

        group = None
        for temp in result:
            if temp.name == self.affinity_group_name:
                group = temp
                break

        self.assertIsNotNone(group)
        self.assertIsNotNone(group.name)
        self.assertIsNotNone(group.label)
        self.assertIsNotNone(group.description)
        self.assertIsNotNone(group.location)
        self.assertIsNotNone(group.capabilities)
        self.assertTrue(len(group.capabilities) > 0)

    def test_get_affinity_group_properties(self):
        # Arrange
        self.hosted_service_name = getUniqueNameBasedOnCurrentTime('utsvc')
        self.storage_account_name = getUniqueNameBasedOnCurrentTime('utstorage')
        self._create_affinity_group(self.affinity_group_name)
        self.sms.create_hosted_service(self.hosted_service_name, 'affgrptestlabel', 'affgrptestdesc', None, self.affinity_group_name)
        self.sms.create_storage_account(self.storage_account_name, self.storage_account_name + 'desc', self.storage_account_name + 'label', self.affinity_group_name)

        # Act
        result = self.sms.get_affinity_group_properties(self.affinity_group_name)
        
        # Assert
        self.assertIsNotNone(result)
        self.assertEqual(result.name, self.affinity_group_name)
        self.assertIsNotNone(result.label)
        self.assertIsNotNone(result.description)
        self.assertIsNotNone(result.location)
        self.assertIsNotNone(result.hosted_services[0])
        self.assertEqual(result.hosted_services[0].service_name, self.hosted_service_name)
        self.assertEqual(result.hosted_services[0].hosted_service_properties.affinity_group, self.affinity_group_name)
        # not sure why azure does not return any storage service
        self.assertTrue(len(result.capabilities) > 0)

    def test_create_affinity_group(self):
        # Arrange
        label = 'tstmgmtaffgrp'
        description = 'tstmgmt affinity group'

        # Act
        result = self.sms.create_affinity_group(self.affinity_group_name, label, 'West US', description)

        # Assert
        self.assertIsNone(result)
        self.assertTrue(self._affinity_group_exists(self.affinity_group_name))

    def test_update_affinity_group(self):
        # Arrange
        self._create_affinity_group(self.affinity_group_name)
        label = 'tstlabelupdate'
        description = 'testmgmt affinity group update'

        # Act
        result = self.sms.update_affinity_group(self.affinity_group_name, label, description)

        # Assert
        self.assertIsNone(result)
        props = self.sms.get_affinity_group_properties(self.affinity_group_name)
        self.assertEqual(props.label, label)
        self.assertEqual(props.description, description)

    def test_delete_affinity_group(self):
        # Arrange
        self._create_affinity_group(self.affinity_group_name)

        # Act
        result = self.sms.delete_affinity_group(self.affinity_group_name)

        # Assert
        self.assertIsNone(result)
        self.assertFalse(self._affinity_group_exists(self.affinity_group_name))

    #--Test cases for locations ------------------------------------------
    def test_list_locations(self):
        # Arrange

        # Act
        result = self.sms.list_locations()
        
        # Assert
        self.assertIsNotNone(result)
        self.assertTrue(len(result) > 0)
        self.assertIsNotNone(result[0].name)
        self.assertIsNotNone(result[0].display_name)
        self.assertIsNotNone(result[0].available_services)
        self.assertTrue(len(result[0].available_services) > 0)
class AzureServicesManager:
    # Storage
    container = 'vhds'
    windows_blob_url = 'blob.core.windows.net'

    # Linux
    linux_user = '******'
    linux_pass = '******'
    location = 'West US'
    # SSH Keys

    def __init__(self, subscription_id, cert_file):
        self.subscription_id = subscription_id
        self.cert_file = cert_file
        self.sms = ServiceManagementService(self.subscription_id, self.cert_file)

    @property
    def sms(self):
         return self.sms

    def list_locations(self):
        locations = self.sms.list_locations()
        for location in locations:
            print location

    def list_images(self):
        return self.sms.list_os_images()

    @utils.resource_not_found_handler
    def get_hosted_service(self, service_name):
        resp = self.sms.get_hosted_service_properties(service_name)
        properties = resp.hosted_service_properties
        return properties.__dict__

    def delete_hosted_service(self, service_name):
        res = self.sms.check_hosted_service_name_availability(service_name)
        if not res.result:
            return

        self.sms.delete_hosted_service(service_name)

    def create_hosted_service(self, os_user, service_name=None, random=False):
        if not service_name:
            service_name = self.generate_cloud_service_name(os_user, random)

        available = False

        while not available:
            res = self.sms.check_hosted_service_name_availability(service_name)
            if not res.result:
                service_name = self.generate_cloud_service_name(os_user,
                                                                random)
            else:
                available = True

        self.sms.create_hosted_service(service_name=service_name,
                                       label=service_name,
                                       location='West US')

        return service_name

    def create_virtual_machine(self, service_name, vm_name, image_name, role_size):
        media_link = self._get_media_link(vm_name)
        # Linux VM configuration
        hostname = '-'.join((vm_name, 'host'))
        linux_config = LinuxConfigurationSet(hostname,
                                             self.linux_user,
                                             self.linux_pass,
                                             True)

        # Hard disk for the OS
        os_hd = OSVirtualHardDisk(image_name, media_link)

        # Create vm
        result = self.sms.create_virtual_machine_deployment(
            service_name=service_name,  deployment_name=vm_name,
            deployment_slot='production', label=vm_name,
            role_name=vm_name, system_config=linux_config,
            os_virtual_hard_disk=os_hd,
            role_size=role_size
        )
        request_id = result.request_id

        return {
            'request_id': request_id,
            'media_link': media_link
        }

    def delete_virtual_machine(self, service_name, vm_name):
        resp = self.sms.delete_deployment(service_name, vm_name, True)
        self.sms.wait_for_operation_status(resp.request_id)
        result = self.sms.delete_hosted_service(service_name)
        return result

    def generate_cloud_service_name(self, os_user=None, random=False):
        if random:
            return utils.generate_random_name(10)

        return '-'.join((os_user, utils.generate_random_name(6)))

    @utils.resource_not_found_handler
    def get_virtual_machine_info(self, service_name, vm_name):
        vm_info = {}
        deploy_info = self.sms.get_deployment_by_name(service_name, vm_name)

        if deploy_info and deploy_info.role_instance_list:
            vm_info = deploy_info.role_instance_list[0].__dict__

        return vm_info

    def list_virtual_machines(self):
        vm_list = []
        services = self.sms.list_hosted_services()
        for service in services:
            deploys = service.deployments
            if deploys and deploys.role_instance_list:
                vm_name = deploys.role_instance_list[0].instance_name
                vm_list.append(vm_name)

        return vm_list

    def power_on(self, service_name, vm_name):
        resp = self.sms.start_role(service_name, vm_name, vm_name)
        return resp.request_id

    def power_off(self, service_name, vm_name):
        resp = self.sms.shutdown_role(service_name, vm_name, vm_name)
        return resp.request_id

    def soft_reboot(self, service_name, vm_name):
        resp = self.sms.restart_role(service_name, vm_name, vm_name)
        return resp.request_id

    def hard_reboot(self, service_name, vm_name):
        resp = self.sms.reboot_role_instance(service_name, vm_name, vm_name)
        return resp.request_id

    def attach_volume(self, service_name, vm_name, size, lun):
        disk_name = utils.generate_random_name(5, vm_name)
        media_link = self._get_media_link(vm_name, disk_name)

        self.sms.add_data_disk(service_name,
                               vm_name,
                               vm_name,
                               lun,
                               host_caching='ReadWrite',
                               media_link=media_link,
                               disk_name=disk_name,
                               logical_disk_size_in_gb=size)

    def detach_volume(self, service_name, vm_name, lun):
        self.sms.delete_data_disk(service_name, vm_name, vm_name, lun, True)

    def get_available_lun(self, service_name, vm_name):
        try:
            role = self.sms.get_role(service_name, vm_name, vm_name)
        except Exception:
            return 0

        disks = role.data_virtual_hard_disks
        luns = [disk.lun for disk in disks].sort()

        for i in range(1, 16):
            if i not in luns:
                return i

        return None

    def snapshot(self, service_name, vm_name, image_id, snanshot_name):
        image_desc = 'Snapshot for image %s' % vm_name
        image = CaptureRoleAsVMImage('Specialized', snanshot_name,
                                     image_id, image_desc, 'english')

        resp = self.sms.capture_vm_image(service_name, vm_name, vm_name, image)

        self.sms.wait_for_operation_status(resp.request_id)

    def _get_media_link(self, vm_name, filename=None, storage_account=None):
        """ The MediaLink should be constructed as:
        https://<storageAccount>.<blobLink>/<blobContainer>/<filename>.vhd
        """
        if not storage_account:
            storage_account = self._get_or_create_storage_account()

        container = self.container
        filename = vm_name if filename is None else filename
        blob = vm_name + '-' + filename + '.vhd'
        media_link = "http://%s.%s/%s/%s" % (storage_account,
                                             self.windows_blob_url,
                                             container, blob)

        return media_link

    def _get_or_create_storage_account(self):
        account_list = self.sms.list_storage_accounts()

        if account_list:
            return account_list[-1].service_name

        storage_account = utils.generate_random_name(10)
        description = "Storage account %s description" % storage_account
        label = storage_account + 'label'
        self.sms.create_storage_account(storage_account,
                                        description,
                                        label,
                                        location=self.location)

        return storage_account

    def _wait_for_operation(self, request_id, timeout=3000,
                            failure_callback=None,
                            failure_callback_kwargs=None):
        try:
            self.sms.wait_for_operation_status(request_id, timeout=timeout)
        except Exception as ex:
            if failure_callback and failure_callback_kwargs:
                failure_callback(**failure_callback_kwargs)

            raise ex
예제 #4
0
class Deployment(object):
    """
    Helper class to handle deployment of the web site.
    """
    def __init__(self, config):
        self.config = config
        self.sms = ServiceManagementService(config.getAzureSubscriptionId(),
                                            config.getAzureCertificatePath())
        self.sbms = ServiceBusManagementService(
            config.getAzureSubscriptionId(), config.getAzureCertificatePath())

    @staticmethod
    def _resource_exists(get_resource):
        """
        Helper to check for the existence of a resource in Azure.

        get_resource: Parameter-less function to invoke in order to get the resource. The resource
            is assumed to exist when the call to get_resource() returns a value that is not None.
            If the call to get_resource() returns None or throws a WindowsAzureMissingResourceError
            exception, then it is assumed that the resource does not exist.

        Returns: A boolean value which is True if the resource exists.
        """
        resource = None
        try:
            resource = get_resource()
        except WindowsAzureMissingResourceError:
            pass
        return resource is not None

    def _wait_for_operation_success(self, request_id, timeout=600, wait=5):
        """
        Waits for an asynchronous Azure operation to finish.

        request_id: The ID of the request to track.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to fetch the latest operation status.
        """
        result = self.sms.get_operation_status(request_id)
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        while result.status == 'InProgress':
            if now >= max_time:
                raise Exception(
                    "Operation did not finish within the expected timeout")
            logger.info(
                'Waiting for operation to finish (last_status=%s wait_so_far=%s)',
                result.status, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            result = self.sms.get_operation_status(request_id)
            now = time.time()
        if result.status != 'Succeeded':
            raise Exception("Operation terminated but it did not succeed.")

    def _wait_for_role_instance_status(self,
                                       role_instance_name,
                                       service_name,
                                       expected_status,
                                       timeout=600,
                                       wait=5):
        """
        Waits for a role instance within the web site's cloud service to reach the status specified.

        role_instance_name: Name of the role instance.
        service_name: Name of service in which to find the role instance.
        expected_status: Expected instance status.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to fetch the latest role status.
        """
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        while True:
            status = None
            deployment = self.sms.get_deployment_by_name(
                service_name, service_name)
            for role_instance in deployment.role_instance_list:
                if role_instance.instance_name == role_instance_name:
                    status = role_instance.instance_status
            if status == expected_status:
                break
            if now >= max_time:
                raise Exception(
                    "Operation did not finish within the expected timeout")
            logger.info(
                'Waiting for deployment status: expecting %s but got %s (wait_so_far=%s)',
                expected_status, status, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            now = time.time()

    def _wait_for_disk_deletion(self, disk_name, timeout=600, wait=5):
        """
        Waits for a VM disk to disappear when it is being deleted.

        disk_name: Name of the VHD.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to check for the existence of the disk.
        """
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        logger.info("Checking that disk %s has been deleted.", disk_name)
        while self._resource_exists(lambda: self.sms.get_disk(disk_name)):
            if now >= max_time:
                raise Exception(
                    "Disk %s was not deleted within the expected timeout.".
                    format(disk_name))
            logger.info("Waiting for disk %s to disappear (wait_so_far=%s).",
                        disk_name, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            now = time.time()
        logger.info("Disk %s has been deleted.", disk_name)

    def _wait_for_namespace_active(self, name, timeout=600, wait=5):
        """
        Waits for a service bus namespace to become Active.

        name: Namespace name.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to check for the existence of the disk.
        """
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        while True:
            status = None
            props = self.sbms.get_namespace(name)
            status = props.status
            if status == 'Active':
                break
            if now >= max_time:
                raise Exception(
                    "Operation did not finish within the expected timeout")
            logger.info(
                'Waiting for namepsace status: expecting Active but got %s (wait_so_far=%s)',
                status, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            now = time.time()

    def _getRoleInstances(self, service_name):
        """
        Returns the role instances in the given cloud service deployment. The results are provided as
        a dictionary where keys are role instance names and values are RoleInstance objects.
        """
        role_instances = {}
        if self._resource_exists(lambda: self.sms.get_deployment_by_name(
                service_name, service_name)):
            deployment = self.sms.get_deployment_by_name(
                service_name, service_name)
            for role_instance in deployment.role_instance_list:
                role_instances[role_instance.instance_name] = role_instance
        return role_instances

    def _ensureAffinityGroupExists(self):
        """
        Creates the affinity group if it does not exist.
        """
        name = self.config.getAffinityGroupName()
        location = self.config.getServiceLocation()
        logger.info(
            "Checking for existence of affinity group (name=%s; location=%s).",
            name, location)
        if self._resource_exists(
                lambda: self.sms.get_affinity_group_properties(name)):
            logger.warn("An affinity group named %s already exists.", name)
        else:
            self.sms.create_affinity_group(name, name, location)
            logger.info("Created affinity group %s.", name)

    def _ensureStorageAccountExists(self, name):
        """
        Creates the storage account if it does not exist.
        """
        logger.info("Checking for existence of storage account (name=%s).",
                    name)
        if self._resource_exists(
                lambda: self.sms.get_storage_account_properties(name)):
            logger.warn("A storage account named %s already exists.", name)
        else:
            result = self.sms.create_storage_account(
                name,
                "",
                name,
                affinity_group=self.config.getAffinityGroupName())
            self._wait_for_operation_success(
                result.request_id,
                timeout=self.config.getAzureOperationTimeout())
            logger.info("Created storage account %s.", name)

    def _getStorageAccountKey(self, account_name):
        """
        Gets the storage account key (primary key) for the given storage account.
        """
        storage_props = self.sms.get_storage_account_keys(account_name)
        return storage_props.storage_service_keys.primary

    def _ensureStorageContainersExist(self):
        """
        Creates Blob storage containers required by the service.
        """
        logger.info("Checking for existence of Blob containers.")
        account_name = self.config.getServiceStorageAccountName()
        account_key = self._getStorageAccountKey(account_name)
        blob_service = BlobService(account_name, account_key)
        name_and_access_list = [
            (self.config.getServicePublicStorageContainer(), 'blob'),
            (self.config.getServiceBundleStorageContainer(), None)
        ]
        for name, access in name_and_access_list:
            logger.info("Checking for existence of Blob container %s.", name)
            blob_service.create_container(name,
                                          x_ms_blob_public_access=access,
                                          fail_on_exist=False)
            access_info = 'private' if access is None else 'public {0}'.format(
                access)
            logger.info("Blob container %s is ready (access: %s).", name,
                        access_info)

    def ensureStorageHasCorsConfiguration(self):
        """
        Ensures Blob storage container for bundles is configured to allow cross-origin resource sharing.
        """
        logger.info("Setting CORS rules.")
        account_name = self.config.getServiceStorageAccountName()
        account_key = self._getStorageAccountKey(account_name)

        cors_rule = CorsRule()
        cors_rule.allowed_origins = self.config.getServiceStorageCorsAllowedOrigins(
        )
        cors_rule.allowed_methods = 'PUT'
        cors_rule.exposed_headers = '*'
        cors_rule.allowed_headers = '*'
        cors_rule.max_age_in_seconds = 1800
        cors_rules = Cors()
        cors_rules.cors_rule.append(cors_rule)
        set_storage_service_cors_properties(account_name, account_key,
                                            cors_rules)

    def _ensureServiceExists(self, service_name, affinity_group_name):
        """
        Creates the specified cloud service host if it does not exist.

        service_name: Name of the cloud service.
        affinity_group_name: Name of the affinity group (which should exists).
        """
        logger.info("Checking for existence of cloud service (name=%s).",
                    service_name)
        if self._resource_exists(
                lambda: self.sms.get_hosted_service_properties(service_name)):
            logger.warn("A cloud service named %s already exists.",
                        service_name)
        else:
            self.sms.create_hosted_service(service_name,
                                           service_name,
                                           affinity_group=affinity_group_name)
            logger.info("Created cloud service %s.", service_name)

    def _ensureServiceCertificateExists(self, service_name):
        """
        Adds certificate to the specified cloud service.

        service_name: Name of the target cloud service (which should exist).
        """
        cert_format = self.config.getServiceCertificateFormat()
        cert_algorithm = self.config.getServiceCertificateAlgorithm()
        cert_thumbprint = self.config.getServiceCertificateThumbprint()
        cert_path = self.config.getServiceCertificateFilename()
        cert_password = self.config.getServiceCertificatePassword()
        logger.info(
            "Checking for existence of cloud service certificate for service %s.",
            service_name)
        get_cert = lambda: self.sms.get_service_certificate(
            service_name, cert_algorithm, cert_thumbprint)
        if self._resource_exists(get_cert):
            logger.info("Found expected cloud service certificate.")
        else:
            with open(cert_path, 'rb') as f:
                cert_data = base64.b64encode(f.read())
            if len(cert_data) <= 0:
                raise Exception("Detected invalid certificate data.")
            result = self.sms.add_service_certificate(service_name, cert_data,
                                                      cert_format,
                                                      cert_password)
            self._wait_for_operation_success(
                result.request_id,
                timeout=self.config.getAzureOperationTimeout())
            logger.info("Added service certificate.")

    def _assertOsImageExists(self, os_image_name):
        """
        Asserts that the named OS image exists.
        """
        logger.info("Checking for availability of OS image (name=%s).",
                    os_image_name)
        if self.sms.get_os_image(os_image_name) is None:
            raise Exception(
                "Unable to find OS Image '{0}'.".format(os_image_name))

    def _ensureVirtualMachinesExist(self):
        """
        Creates the VMs for the web site.
        """
        service_name = self.config.getServiceName()
        cert_thumbprint = self.config.getServiceCertificateThumbprint()
        vm_username = self.config.getVirtualMachineLogonUsername()
        vm_password = self.config.getVirtualMachineLogonPassword()
        vm_role_size = self.config.getServiceInstanceRoleSize()
        vm_numbers = self.config.getServiceInstanceCount()
        if vm_numbers < 1:
            raise Exception(
                "Detected an invalid number of instances: {0}.".format(
                    vm_numbers))

        self._assertOsImageExists(self.config.getServiceOSImageName())

        role_instances = self._getRoleInstances(service_name)
        for vm_number in range(1, vm_numbers + 1):
            vm_hostname = '{0}-{1}'.format(service_name, vm_number)
            if vm_hostname in role_instances:
                logger.warn(
                    "Role instance %s already exists: skipping creation.",
                    vm_hostname)
                continue

            logger.info("Role instance %s provisioning begins.", vm_hostname)
            vm_diskname = '{0}.vhd'.format(vm_hostname)
            vm_disk_media_link = 'http://{0}.blob.core.windows.net/vhds/{1}'.format(
                self.config.getServiceStorageAccountName(), vm_diskname)
            ssh_port = str(self.config.getServiceInstanceSshPort() + vm_number)

            os_hd = OSVirtualHardDisk(self.config.getServiceOSImageName(),
                                      vm_disk_media_link,
                                      disk_name=vm_diskname,
                                      disk_label=vm_diskname)
            linux_config = LinuxConfigurationSet(vm_hostname, vm_username,
                                                 vm_password, True)
            linux_config.ssh.public_keys.public_keys.append(
                PublicKey(
                    cert_thumbprint,
                    u'/home/{0}/.ssh/authorized_keys'.format(vm_username)))
            linux_config.ssh.key_pairs.key_pairs.append(
                KeyPair(cert_thumbprint,
                        u'/home/{0}/.ssh/id_rsa'.format(vm_username)))
            network_config = ConfigurationSet()
            network_config.configuration_set_type = 'NetworkConfiguration'
            ssh_endpoint = ConfigurationSetInputEndpoint(name='SSH',
                                                         protocol='TCP',
                                                         port=ssh_port,
                                                         local_port=u'22')
            network_config.input_endpoints.input_endpoints.append(ssh_endpoint)
            http_endpoint = ConfigurationSetInputEndpoint(
                name='HTTP',
                protocol='TCP',
                port=u'80',
                local_port=u'80',
                load_balanced_endpoint_set_name=service_name)
            http_endpoint.load_balancer_probe.port = '80'
            http_endpoint.load_balancer_probe.protocol = 'TCP'
            network_config.input_endpoints.input_endpoints.append(
                http_endpoint)

            if vm_number == 1:
                result = self.sms.create_virtual_machine_deployment(
                    service_name=service_name,
                    deployment_name=service_name,
                    deployment_slot='Production',
                    label=vm_hostname,
                    role_name=vm_hostname,
                    system_config=linux_config,
                    os_virtual_hard_disk=os_hd,
                    network_config=network_config,
                    availability_set_name=service_name,
                    data_virtual_hard_disks=None,
                    role_size=vm_role_size)
                self._wait_for_operation_success(
                    result.request_id,
                    timeout=self.config.getAzureOperationTimeout())
                self._wait_for_role_instance_status(
                    vm_hostname, service_name, 'ReadyRole',
                    self.config.getAzureOperationTimeout())
            else:
                result = self.sms.add_role(service_name=service_name,
                                           deployment_name=service_name,
                                           role_name=vm_hostname,
                                           system_config=linux_config,
                                           os_virtual_hard_disk=os_hd,
                                           network_config=network_config,
                                           availability_set_name=service_name,
                                           role_size=vm_role_size)
                self._wait_for_operation_success(
                    result.request_id,
                    timeout=self.config.getAzureOperationTimeout())
                self._wait_for_role_instance_status(
                    vm_hostname, service_name, 'ReadyRole',
                    self.config.getAzureOperationTimeout())

            logger.info("Role instance %s has been created.", vm_hostname)

    def _deleteVirtualMachines(self, service_name):
        """
        Deletes the VMs in the given cloud service.
        """
        if self._resource_exists(lambda: self.sms.get_deployment_by_name(
                service_name, service_name)) == False:
            logger.warn("Deployment %s not found: no VMs to delete.",
                        service_name)
        else:
            logger.info("Attempting to delete deployment %s.", service_name)
            # Get set of role instances before we remove them
            role_instances = self._getRoleInstances(service_name)

            def update_request(request):
                """
                A filter to intercept the HTTP request sent by the ServiceManagementService
                so we can take advantage of a newer feature ('comp=media') in the delete deployment API
                (see http://msdn.microsoft.com/en-us/library/windowsazure/ee460812.aspx)
                """
                hdrs = []
                for name, value in request.headers:
                    if 'x-ms-version' == name:
                        value = '2013-08-01'
                    hdrs.append((name, value))
                request.headers = hdrs
                request.path = request.path + '?comp=media'
                #pylint: disable=W0212
                response = self.sms._filter(request)
                return response

            svc = ServiceManagementService(self.sms.subscription_id,
                                           self.sms.cert_file)
            #pylint: disable=W0212
            svc._filter = update_request
            result = svc.delete_deployment(service_name, service_name)
            logger.info(
                "Deployment %s deletion in progress: waiting for delete_deployment operation.",
                service_name)
            self._wait_for_operation_success(result.request_id)
            logger.info(
                "Deployment %s deletion in progress: waiting for VM disks to be removed.",
                service_name)
            # Now wait for the disks to disappear
            for role_instance_name in role_instances.keys():
                disk_name = "{0}.vhd".format(role_instance_name)
                self._wait_for_disk_deletion(disk_name)
            logger.info("Deployment %s deleted.", service_name)

    def _ensureBuildMachineExists(self):
        """
        Creates the VM for the build server.
        """
        service_name = self.config.getBuildServiceName()
        service_storage_name = self.config.getStorageAccountName()
        cert_thumbprint = self.config.getServiceCertificateThumbprint()
        vm_username = self.config.getVirtualMachineLogonUsername()
        vm_password = self.config.getVirtualMachineLogonPassword()
        vm_hostname = service_name

        role_instances = self._getRoleInstances(service_name)
        if vm_hostname in role_instances:
            logger.warn("Role instance %s already exists: skipping creation.",
                        vm_hostname)
        else:
            logger.info("Role instance %s provisioning begins.", vm_hostname)
            self._assertOsImageExists(self.config.getBuildOSImageName())

            vm_diskname = '{0}.vhd'.format(vm_hostname)
            vm_disk_media_link = 'http://{0}.blob.core.windows.net/vhds/{1}'.format(
                service_storage_name, vm_diskname)
            os_hd = OSVirtualHardDisk(self.config.getBuildOSImageName(),
                                      vm_disk_media_link,
                                      disk_name=vm_diskname,
                                      disk_label=vm_diskname)
            linux_config = LinuxConfigurationSet(vm_hostname, vm_username,
                                                 vm_password, True)
            linux_config.ssh.public_keys.public_keys.append(
                PublicKey(
                    cert_thumbprint,
                    u'/home/{0}/.ssh/authorized_keys'.format(vm_username)))
            linux_config.ssh.key_pairs.key_pairs.append(
                KeyPair(cert_thumbprint,
                        u'/home/{0}/.ssh/id_rsa'.format(vm_username)))
            network_config = ConfigurationSet()
            network_config.configuration_set_type = 'NetworkConfiguration'
            ssh_endpoint = ConfigurationSetInputEndpoint(name='SSH',
                                                         protocol='TCP',
                                                         port=u'22',
                                                         local_port=u'22')
            network_config.input_endpoints.input_endpoints.append(ssh_endpoint)

            result = self.sms.create_virtual_machine_deployment(
                service_name=service_name,
                deployment_name=service_name,
                deployment_slot='Production',
                label=vm_hostname,
                role_name=vm_hostname,
                system_config=linux_config,
                os_virtual_hard_disk=os_hd,
                network_config=network_config,
                availability_set_name=None,
                data_virtual_hard_disks=None,
                role_size=self.config.getBuildInstanceRoleSize())
            self._wait_for_operation_success(
                result.request_id,
                timeout=self.config.getAzureOperationTimeout())
            self._wait_for_role_instance_status(
                vm_hostname, service_name, 'ReadyRole',
                self.config.getAzureOperationTimeout())
            logger.info("Role instance %s has been created.", vm_hostname)

    def _deleteStorageAccount(self, name):
        """
        Deletes the storage account for the web site.
        """
        logger.info("Attempting to delete storage account %s.", name)
        if self._resource_exists(
                lambda: self.sms.get_storage_account_properties(name
                                                                )) == False:
            logger.warn("Storage account %s not found: nothing to delete.",
                        name)
        else:
            self.sms.delete_storage_account(name)
            logger.info("Storage account %s deleted.", name)

    def _deleteService(self, name):
        """
        Deletes the specified cloud service.
        """
        logger.info("Attempting to delete cloud service %s.", name)
        if self._resource_exists(
                lambda: self.sms.get_hosted_service_properties(name)) == False:
            logger.warn("Cloud service %s not found: nothing to delete.", name)
        else:
            self.sms.delete_hosted_service(name)
            logger.info("Cloud service %s deleted.", name)

    def _deleteAffinityGroup(self):
        """
        Deletes the affinity group for the web site.
        """
        name = self.config.getAffinityGroupName()
        logger.info("Attempting to delete affinity group %s.", name)
        if self._resource_exists(
                lambda: self.sms.get_affinity_group_properties(name)) == False:
            logger.warn("Affinity group %s not found: nothing to delete.",
                        name)
        else:
            self.sms.delete_affinity_group(name)
            logger.info("Affinity group %s deleted.", name)

    def _ensureServiceBusNamespaceExists(self):
        """
        Creates the Azure Service Bus Namespace if it does not exist.
        """
        name = self.config.getServiceBusNamespace()
        logger.info(
            "Checking for existence of service bus namespace (name=%s).", name)
        if self._resource_exists(lambda: self.sbms.get_namespace(name)):
            logger.warn("A namespace named %s already exists.", name)
        else:
            self.sbms.create_namespace(name, self.config.getServiceLocation())
            self._wait_for_namespace_active(name)
            logger.info("Created namespace %s.", name)

    def _ensureServiceBusQueuesExist(self):
        """
        Creates Azure service bus queues required by the service.
        """
        logger.info("Checking for existence of Service Bus Queues.")
        namespace = self.sbms.get_namespace(
            self.config.getServiceBusNamespace())
        sbs = ServiceBusService(namespace.name,
                                namespace.default_key,
                                issuer='owner')
        queue_names = [
            'jobresponsequeue', 'windowscomputequeue', 'linuxcomputequeue'
        ]
        for name in queue_names:
            logger.info("Checking for existence of Queue %s.", name)
            sbs.create_queue(name, fail_on_exist=False)
            logger.info("Queue %s is ready.", name)

    def _deleteServiceBusNamespace(self):
        """
        Deletes the Azure Service Bus Namespace.
        """
        name = self.config.getServiceBusNamespace()
        logger.info("Attempting to delete service bus namespace %s.", name)
        if self._resource_exists(
                lambda: self.sbms.get_namespace(name)) == False:
            logger.warn("Namespace %s not found: nothing to delete.", name)
        else:
            self.sbms.delete_namespace(name)
            logger.info("Namespace %s deleted.", name)

    def Deploy(self, assets):
        """
        Creates a deployment.

        assets: The set of assets to create. The full set is: {'build', 'web'}.
        """
        if len(assets) == 0:
            raise ValueError("Set of assets to deploy is not specified.")
        logger.info("Starting deployment operation.")
        self._ensureAffinityGroupExists()
        self._ensureStorageAccountExists(self.config.getStorageAccountName())
        ## Build instance
        if 'build' in assets:
            self._ensureServiceExists(self.config.getBuildServiceName(),
                                      self.config.getAffinityGroupName())
            self._ensureServiceCertificateExists(
                self.config.getBuildServiceName())
            self._ensureBuildMachineExists()
        # Web instances
        if 'web' in assets:
            self._ensureStorageAccountExists(
                self.config.getServiceStorageAccountName())
            self._ensureStorageContainersExist()
            self.ensureStorageHasCorsConfiguration()
            self._ensureServiceBusNamespaceExists()
            self._ensureServiceBusQueuesExist()
            self._ensureServiceExists(self.config.getServiceName(),
                                      self.config.getAffinityGroupName())
            self._ensureServiceCertificateExists(self.config.getServiceName())
            self._ensureVirtualMachinesExist()
        #queues
        logger.info("Deployment operation is complete.")

    def Teardown(self, assets):
        """
        Deletes a deployment.

        assets: The set of assets to delete. The full set is: {'web', 'build'}.
        """
        if len(assets) == 0:
            raise ValueError("Set of assets to teardown is not specified.")
        logger.info("Starting teardown operation.")
        if 'web' in assets:
            self._deleteVirtualMachines(self.config.getServiceName())
            self._deleteService(self.config.getServiceName())
            self._deleteStorageAccount(
                self.config.getServiceStorageAccountName())
        if 'build' in assets:
            self._deleteVirtualMachines(self.config.getBuildServiceName())
            self._deleteService(self.config.getBuildServiceName())
            self._deleteStorageAccount(self.config.getStorageAccountName())
        if ('web' in assets) and ('build' in assets):
            self._deleteServiceBusNamespace()
            self._deleteAffinityGroup()
        logger.info("Teardown operation is complete.")

    def getSettingsFileContent(self):
        """
        Generates the content of the local Django settings file.
        """
        allowed_hosts = [
            '{0}.cloudapp.net'.format(self.config.getServiceName())
        ]
        allowed_hosts.extend(self.config.getWebHostnames())
        allowed_hosts.extend(['www.codalab.org', 'codalab.org'])
        ssl_allowed_hosts = self.config.getSslRewriteHosts()
        if len(ssl_allowed_hosts) == 0:
            ssl_allowed_hosts = allowed_hosts

        storage_key = self._getStorageAccountKey(
            self.config.getServiceStorageAccountName())
        namespace = self.sbms.get_namespace(
            self.config.getServiceBusNamespace())

        if len(self.config.getSslCertificateInstalledPath()) > 0:
            bundle_auth_scheme = "https"
        else:
            bundle_auth_scheme = "http"
        if len(ssl_allowed_hosts) == 0:
            bundle_auth_host = '{0}.cloudapp.net'.format(
                self.config.getServiceName())
        else:
            bundle_auth_host = ssl_allowed_hosts[0]
        bundle_auth_url = "{0}://{1}".format(bundle_auth_scheme,
                                             bundle_auth_host)

        lines = [
            "from base import Base",
            "from default import *",
            "from configurations import Settings",
            "",
            "import sys",
            "from os.path import dirname, abspath, join",
            "from pkgutil import extend_path",
            "import codalab",
            "",
            "class {0}(Base):".format(self.config.getDjangoConfiguration()),
            "",
            "    DEBUG=False",
            "",
            "    ALLOWED_HOSTS = {0}".format(allowed_hosts),
            "",
            "    SSL_PORT = '443'",
            "    SSL_CERTIFICATE = '{0}'".format(
                self.config.getSslCertificateInstalledPath()),
            "    SSL_CERTIFICATE_KEY = '{0}'".format(
                self.config.getSslCertificateKeyInstalledPath()),
            "    SSL_ALLOWED_HOSTS = {0}".format(ssl_allowed_hosts),
            "",
            "    DEFAULT_FILE_STORAGE = 'codalab.azure_storage.AzureStorage'",
            "    AZURE_ACCOUNT_NAME = '{0}'".format(
                self.config.getServiceStorageAccountName()),
            "    AZURE_ACCOUNT_KEY = '{0}'".format(storage_key),
            "    AZURE_CONTAINER = '{0}'".format(
                self.config.getServicePublicStorageContainer()),
            "    BUNDLE_AZURE_ACCOUNT_NAME = AZURE_ACCOUNT_NAME",
            "    BUNDLE_AZURE_ACCOUNT_KEY = AZURE_ACCOUNT_KEY",
            "    BUNDLE_AZURE_CONTAINER = '{0}'".format(
                self.config.getServiceBundleStorageContainer()),
            "",
            "    SBS_NAMESPACE = '{0}'".format(
                self.config.getServiceBusNamespace()),
            "    SBS_ISSUER = 'owner'",
            "    SBS_ACCOUNT_KEY = '{0}'".format(namespace.default_key),
            "    SBS_RESPONSE_QUEUE = 'jobresponsequeue'",
            "    SBS_COMPUTE_QUEUE = 'windowscomputequeue'",
            "",
            "    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'",
            "    EMAIL_HOST = '{0}'".format(self.config.getEmailHost()),
            "    EMAIL_HOST_USER = '******'".format(self.config.getEmailUser()),
            "    EMAIL_HOST_PASSWORD = '******'".format(
                self.config.getEmailPassword()),
            "    EMAIL_PORT = 587",
            "    EMAIL_USE_TLS = True",
            "    DEFAULT_FROM_EMAIL = '*****@*****.**'",
            "    SERVER_EMAIL = '*****@*****.**'",
            "",
            "    # Django secret",
            "    SECRET_KEY = '{0}'".format(self.config.getDjangoSecretKey()),
            "",
            "    ADMINS = (('CodaLab', '*****@*****.**'),)",
            "    MANAGERS = ADMINS",
            "",
            "    DATABASES = {",
            "        'default': {",
            "            'ENGINE': '{0}',".format(
                self.config.getDatabaseEngine()),
            "            'NAME': '{0}',".format(self.config.getDatabaseName()),
            "            'USER': '******',".format(self.config.getDatabaseUser()),
            "            'PASSWORD': '******',".format(
                self.config.getDatabasePassword()),
            "            'HOST': '{0}',".format(self.config.getDatabaseHost()),
            "            'PORT': '{0}', ".format(
                self.config.getDatabasePort()),
            "            'OPTIONS' : {",
            "                'init_command': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED',",
            "                'read_timeout': 5",
            "            }",
            "        }",
            "    }",
            "",
            "    BUNDLE_DB_NAME = '{0}'".format(
                self.config.getBundleServiceDatabaseName()),
            "    BUNDLE_DB_USER = '******'".format(
                self.config.getBundleServiceDatabaseUser()),
            "    BUNDLE_DB_PASSWORD = '******'".format(
                self.config.getBundleServiceDatabasePassword()),
            "    BUNDLE_APP_ID = '{0}'".format(
                self.config.getBundleServiceAppId()),
            "    BUNDLE_APP_KEY = '{0}'".format(
                self.config.getBundleServiceAppKey()),
            "    BUNDLE_AUTH_URL = '{0}'".format(bundle_auth_url),
            "",
            "    BUNDLE_SERVICE_URL = '{0}'".format(
                self.config.getBundleServiceUrl()),
            "    BUNDLE_SERVICE_CODE_PATH = '/home/{0}/deploy/bundles'".format(
                self.config.getVirtualMachineLogonUsername()),
            "    sys.path.append(BUNDLE_SERVICE_CODE_PATH)",
            "    codalab.__path__ = extend_path(codalab.__path__, codalab.__name__)",
            "",
        ]
        preview = self.config.getShowPreviewFeatures()
        if preview >= 1:
            if preview == 1:
                lines.append("    PREVIEW_WORKSHEETS = True")
            if preview > 1:
                lines.append("    SHOW_BETA_FEATURES = True")
            lines.append("")
        return '\n'.join(lines)
예제 #5
0
파일: __init__.py 프로젝트: prags/codalab
class Deployment(object):
    """
    Helper class to handle deployment of the web site.
    """
    def __init__(self, config):
        self.config = config
        self.sms = ServiceManagementService(config.getAzureSubscriptionId(), config.getAzureCertificatePath())
        self.sbms = ServiceBusManagementService(config.getAzureSubscriptionId(), config.getAzureCertificatePath())

    @staticmethod
    def _resource_exists(get_resource):
        """
        Helper to check for the existence of a resource in Azure.

        get_resource: Parameter-less function to invoke in order to get the resource. The resource
            is assumed to exist when the call to get_resource() returns a value that is not None.
            If the call to get_resource() returns None or throws a WindowsAzureMissingResourceError
            exception, then it is assumed that the resource does not exist.

        Returns: A boolean value which is True if the resource exists.
        """
        resource = None
        try:
            resource = get_resource()
        except WindowsAzureMissingResourceError:
            pass
        return resource is not None

    def _wait_for_operation_success(self, request_id, timeout=600, wait=5):
        """
        Waits for an asynchronous Azure operation to finish.

        request_id: The ID of the request to track.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to fetch the latest operation status.
        """
        result = self.sms.get_operation_status(request_id)
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        while result.status == 'InProgress':
            if now >= max_time:
                raise Exception("Operation did not finish within the expected timeout")
            logger.info('Waiting for operation to finish (last_status=%s wait_so_far=%s)',
                        result.status, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            result = self.sms.get_operation_status(request_id)
            now = time.time()
        if result.status != 'Succeeded':
            raise Exception("Operation terminated but it did not succeed.")

    def _wait_for_role_instance_status(self, role_instance_name, service_name, expected_status, timeout=600, wait=5):
        """
        Waits for a role instance within the web site's cloud service to reach the status specified.

        role_instance_name: Name of the role instance.
        service_name: Name of service in which to find the role instance.
        expected_status: Expected instance status.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to fetch the latest role status.
        """
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        while True:
            status = None
            deployment = self.sms.get_deployment_by_name(service_name, service_name)
            for role_instance in deployment.role_instance_list:
                if role_instance.instance_name == role_instance_name:
                    status = role_instance.instance_status
            if status == expected_status:
                break
            if now >= max_time:
                raise Exception("Operation did not finish within the expected timeout")
            logger.info('Waiting for deployment status: expecting %s but got %s (wait_so_far=%s)',
                        expected_status, status, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            now = time.time()

    def _wait_for_disk_deletion(self, disk_name, timeout=600, wait=5):
        """
        Waits for a VM disk to disappear when it is being deleted.

        disk_name: Name of the VHD.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to check for the existence of the disk.
        """
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        logger.info("Checking that disk %s has been deleted.", disk_name)
        while self._resource_exists(lambda: self.sms.get_disk(disk_name)):
            if now >= max_time:
                raise Exception("Disk %s was not deleted within the expected timeout.".format(disk_name))
            logger.info("Waiting for disk %s to disappear (wait_so_far=%s).", disk_name, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            now = time.time()
        logger.info("Disk %s has been deleted.", disk_name)

    def _wait_for_namespace_active(self, name, timeout=600, wait=5):
        """
        Waits for a service bus namespace to become Active.

        name: Namespace name.
        timeout: Maximum duration (in seconds) allowed for the operation to complete.
        wait: Wait time (in seconds) between consecutive calls to check for the existence of the disk.
        """
        start_time = time.time()
        max_time = start_time + timeout
        now = start_time
        while True:
            status = None
            props = self.sbms.get_namespace(name)
            status = props.status
            if status == 'Active':
                break
            if now >= max_time:
                raise Exception("Operation did not finish within the expected timeout")
            logger.info('Waiting for namespace status: expecting Active but got %s (wait_so_far=%s)',
                        status, round(now - start_time, 1))
            time_to_wait = max(0.0, min(max_time - now, wait))
            time.sleep(time_to_wait)
            now = time.time()

    def _getRoleInstances(self, service_name):
        """
        Returns the role instances in the given cloud service deployment. The results are provided as
        a dictionary where keys are role instance names and values are RoleInstance objects.
        """
        role_instances = {}
        if self._resource_exists(lambda: self.sms.get_deployment_by_name(service_name, service_name)):
            deployment = self.sms.get_deployment_by_name(service_name, service_name)
            for role_instance in deployment.role_instance_list:
                role_instances[role_instance.instance_name] = role_instance
        return role_instances

    def _ensureAffinityGroupExists(self):
        """
        Creates the affinity group if it does not exist.
        """
        name = self.config.getAffinityGroupName()
        location = self.config.getServiceLocation()
        logger.info("Checking for existence of affinity group (name=%s; location=%s).", name, location)
        if self._resource_exists(lambda: self.sms.get_affinity_group_properties(name)):
            logger.warn("An affinity group named %s already exists.", name)
        else:
            self.sms.create_affinity_group(name, name, location)
            logger.info("Created affinity group %s.", name)

    def _ensureStorageAccountExists(self, name):
        """
        Creates the storage account if it does not exist.
        """
        logger.info("Checking for existence of storage account (name=%s).", name)
        if self._resource_exists(lambda: self.sms.get_storage_account_properties(name)):
            logger.warn("A storage account named %s already exists.", name)
        else:
            result = self.sms.create_storage_account(name, "", name, affinity_group=self.config.getAffinityGroupName())
            self._wait_for_operation_success(result.request_id, timeout=self.config.getAzureOperationTimeout())
            logger.info("Created storage account %s.", name)

    def _getStorageAccountKey(self, account_name):
        """
        Gets the storage account key (primary key) for the given storage account.
        """
        storage_props = self.sms.get_storage_account_keys(account_name)
        return storage_props.storage_service_keys.primary

    def _ensureStorageContainersExist(self):
        """
        Creates Blob storage containers required by the service.
        """
        logger.info("Checking for existence of Blob containers.")
        account_name = self.config.getServiceStorageAccountName()
        account_key = self._getStorageAccountKey(account_name)
        blob_service = BlobService(account_name, account_key)
        name_and_access_list = [(self.config.getServicePublicStorageContainer(), 'blob'),
                                (self.config.getServiceBundleStorageContainer(), None)]
        for name, access in name_and_access_list:
            logger.info("Checking for existence of Blob container %s.", name)
            blob_service.create_container(name, x_ms_blob_public_access=access, fail_on_exist=False)
            access_info = 'private' if access is None else 'public {0}'.format(access)
            logger.info("Blob container %s is ready (access: %s).", name, access_info)

    def ensureStorageHasCorsConfiguration(self):
        """
        Ensures Blob storage container for bundles is configured to allow cross-origin resource sharing.
        """
        logger.info("Setting CORS rules.")
        account_name = self.config.getServiceStorageAccountName()
        account_key = self._getStorageAccountKey(account_name)

        cors_rule = CorsRule()
        cors_rule.allowed_origins = self.config.getServiceStorageCorsAllowedOrigins()
        cors_rule.allowed_methods = 'PUT'
        cors_rule.exposed_headers = '*'
        cors_rule.allowed_headers = '*'
        cors_rule.max_age_in_seconds = 1800
        cors_rules = Cors()
        cors_rules.cors_rule.append(cors_rule)
        set_storage_service_cors_properties(account_name, account_key, cors_rules)

    def _ensureServiceExists(self, service_name, affinity_group_name):
        """
        Creates the specified cloud service host if it does not exist.

        service_name: Name of the cloud service.
        affinity_group_name: Name of the affinity group (which should exists).
        """
        logger.info("Checking for existence of cloud service (name=%s).", service_name)
        if self._resource_exists(lambda: self.sms.get_hosted_service_properties(service_name)):
            logger.warn("A cloud service named %s already exists.", service_name)
        else:
            self.sms.create_hosted_service(service_name, service_name, affinity_group=affinity_group_name)
            logger.info("Created cloud service %s.", service_name)

    def _ensureServiceCertificateExists(self, service_name):
        """
        Adds certificate to the specified cloud service.

        service_name: Name of the target cloud service (which should exist).
        """
        cert_format = self.config.getServiceCertificateFormat()
        cert_algorithm = self.config.getServiceCertificateAlgorithm()
        cert_thumbprint = self.config.getServiceCertificateThumbprint()
        cert_path = self.config.getServiceCertificateFilename()
        cert_password = self.config.getServiceCertificatePassword()
        logger.info("Checking for existence of cloud service certificate for service %s.", service_name)
        get_cert = lambda: self.sms.get_service_certificate(service_name, cert_algorithm, cert_thumbprint)
        if self._resource_exists(get_cert):
            logger.info("Found expected cloud service certificate.")
        else:
            with open(cert_path, 'rb') as f:
                cert_data = base64.b64encode(f.read())
            if len(cert_data) <= 0:
                raise Exception("Detected invalid certificate data.")
            result = self.sms.add_service_certificate(service_name, cert_data, cert_format, cert_password)
            self._wait_for_operation_success(result.request_id, timeout=self.config.getAzureOperationTimeout())
            logger.info("Added service certificate.")

    def _assertOsImageExists(self, os_image_name):
        """
        Asserts that the named OS image exists.
        """
        logger.info("Checking for availability of OS image (name=%s).", os_image_name)
        if self.sms.get_os_image(os_image_name) is None:
            raise Exception("Unable to find OS Image '{0}'.".format(os_image_name))

    def _ensureVirtualMachinesExist(self):
        """
        Creates the VMs for the web site.
        """
        service_name = self.config.getServiceName()
        cert_thumbprint = self.config.getServiceCertificateThumbprint()
        vm_username = self.config.getVirtualMachineLogonUsername()
        vm_password = self.config.getVirtualMachineLogonPassword()
        vm_role_size = self.config.getServiceInstanceRoleSize()
        vm_numbers = self.config.getServiceInstanceCount()
        if vm_numbers < 1:
            raise Exception("Detected an invalid number of instances: {0}.".format(vm_numbers))

        self._assertOsImageExists(self.config.getServiceOSImageName())

        role_instances = self._getRoleInstances(service_name)
        for vm_number in range(1, vm_numbers+1):
            vm_hostname = '{0}-{1}'.format(service_name, vm_number)
            if vm_hostname in role_instances:
                logger.warn("Role instance %s already exists: skipping creation.", vm_hostname)
                continue

            logger.info("Role instance %s provisioning begins.", vm_hostname)
            vm_diskname = '{0}.vhd'.format(vm_hostname)
            vm_disk_media_link = 'http://{0}.blob.core.windows.net/vhds/{1}'.format(
                self.config.getServiceStorageAccountName(), vm_diskname
            )
            ssh_port = str(self.config.getServiceInstanceSshPort() + vm_number)

            os_hd = OSVirtualHardDisk(self.config.getServiceOSImageName(),
                                      vm_disk_media_link,
                                      disk_name=vm_diskname,
                                      disk_label=vm_diskname)
            linux_config = LinuxConfigurationSet(vm_hostname, vm_username, vm_password, True)
            linux_config.ssh.public_keys.public_keys.append(
                PublicKey(cert_thumbprint, u'/home/{0}/.ssh/authorized_keys'.format(vm_username))
            )
            linux_config.ssh.key_pairs.key_pairs.append(
                KeyPair(cert_thumbprint, u'/home/{0}/.ssh/id_rsa'.format(vm_username))
            )
            network_config = ConfigurationSet()
            network_config.configuration_set_type = 'NetworkConfiguration'
            ssh_endpoint = ConfigurationSetInputEndpoint(name='SSH',
                                                         protocol='TCP',
                                                         port=ssh_port,
                                                         local_port=u'22')
            network_config.input_endpoints.input_endpoints.append(ssh_endpoint)
            http_endpoint = ConfigurationSetInputEndpoint(name='HTTP',
                                                          protocol='TCP',
                                                          port=u'80',
                                                          local_port=u'80',
                                                          load_balanced_endpoint_set_name=service_name)
            http_endpoint.load_balancer_probe.port = '80'
            http_endpoint.load_balancer_probe.protocol = 'TCP'
            network_config.input_endpoints.input_endpoints.append(http_endpoint)

            if vm_number == 1:
                result = self.sms.create_virtual_machine_deployment(service_name=service_name,
                                                                    deployment_name=service_name,
                                                                    deployment_slot='Production',
                                                                    label=vm_hostname,
                                                                    role_name=vm_hostname,
                                                                    system_config=linux_config,
                                                                    os_virtual_hard_disk=os_hd,
                                                                    network_config=network_config,
                                                                    availability_set_name=service_name,
                                                                    data_virtual_hard_disks=None,
                                                                    role_size=vm_role_size)
                self._wait_for_operation_success(result.request_id,
                                                 timeout=self.config.getAzureOperationTimeout())
                self._wait_for_role_instance_status(vm_hostname, service_name, 'ReadyRole',
                                                    self.config.getAzureOperationTimeout())
            else:
                result = self.sms.add_role(service_name=service_name,
                                           deployment_name=service_name,
                                           role_name=vm_hostname,
                                           system_config=linux_config,
                                           os_virtual_hard_disk=os_hd,
                                           network_config=network_config,
                                           availability_set_name=service_name,
                                           role_size=vm_role_size)
                self._wait_for_operation_success(result.request_id,
                                                 timeout=self.config.getAzureOperationTimeout())
                self._wait_for_role_instance_status(vm_hostname, service_name, 'ReadyRole',
                                                    self.config.getAzureOperationTimeout())

            logger.info("Role instance %s has been created.", vm_hostname)

    def _deleteVirtualMachines(self, service_name):
        """
        Deletes the VMs in the given cloud service.
        """
        if self._resource_exists(lambda: self.sms.get_deployment_by_name(service_name, service_name)) == False:
            logger.warn("Deployment %s not found: no VMs to delete.", service_name)
        else:
            logger.info("Attempting to delete deployment %s.", service_name)
            # Get set of role instances before we remove them
            role_instances = self._getRoleInstances(service_name)

            def update_request(request):
                """
                A filter to intercept the HTTP request sent by the ServiceManagementService
                so we can take advantage of a newer feature ('comp=media') in the delete deployment API
                (see http://msdn.microsoft.com/en-us/library/windowsazure/ee460812.aspx)
                """
                hdrs = []
                for name, value in request.headers:
                    if 'x-ms-version' == name:
                        value = '2013-08-01'
                    hdrs.append((name, value))
                request.headers = hdrs
                request.path = request.path + '?comp=media'
                #pylint: disable=W0212
                response = self.sms._filter(request)
                return response

            svc = ServiceManagementService(self.sms.subscription_id, self.sms.cert_file)
            #pylint: disable=W0212
            svc._filter = update_request
            result = svc.delete_deployment(service_name, service_name)
            logger.info("Deployment %s deletion in progress: waiting for delete_deployment operation.", service_name)
            self._wait_for_operation_success(result.request_id)
            logger.info("Deployment %s deletion in progress: waiting for VM disks to be removed.", service_name)
            # Now wait for the disks to disappear
            for role_instance_name in role_instances.keys():
                disk_name = "{0}.vhd".format(role_instance_name)
                self._wait_for_disk_deletion(disk_name)
            logger.info("Deployment %s deleted.", service_name)

    def _ensureBuildMachineExists(self):
        """
        Creates the VM for the build server.
        """
        service_name = self.config.getBuildServiceName()
        service_storage_name = self.config.getStorageAccountName()
        cert_thumbprint = self.config.getServiceCertificateThumbprint()
        vm_username = self.config.getVirtualMachineLogonUsername()
        vm_password = self.config.getVirtualMachineLogonPassword()
        vm_hostname = service_name

        role_instances = self._getRoleInstances(service_name)
        if vm_hostname in role_instances:
            logger.warn("Role instance %s already exists: skipping creation.", vm_hostname)
        else:
            logger.info("Role instance %s provisioning begins.", vm_hostname)
            self._assertOsImageExists(self.config.getBuildOSImageName())

            vm_diskname = '{0}.vhd'.format(vm_hostname)
            vm_disk_media_link = 'http://{0}.blob.core.windows.net/vhds/{1}'.format(service_storage_name, vm_diskname)
            os_hd = OSVirtualHardDisk(self.config.getBuildOSImageName(),
                                      vm_disk_media_link,
                                      disk_name=vm_diskname,
                                      disk_label=vm_diskname)
            linux_config = LinuxConfigurationSet(vm_hostname, vm_username, vm_password, True)
            linux_config.ssh.public_keys.public_keys.append(
                PublicKey(cert_thumbprint, u'/home/{0}/.ssh/authorized_keys'.format(vm_username))
            )
            linux_config.ssh.key_pairs.key_pairs.append(
                KeyPair(cert_thumbprint, u'/home/{0}/.ssh/id_rsa'.format(vm_username))
            )
            network_config = ConfigurationSet()
            network_config.configuration_set_type = 'NetworkConfiguration'
            ssh_endpoint = ConfigurationSetInputEndpoint(name='SSH',
                                                         protocol='TCP',
                                                         port=u'22',
                                                         local_port=u'22')
            network_config.input_endpoints.input_endpoints.append(ssh_endpoint)

            result = self.sms.create_virtual_machine_deployment(service_name=service_name,
                                                                deployment_name=service_name,
                                                                deployment_slot='Production',
                                                                label=vm_hostname,
                                                                role_name=vm_hostname,
                                                                system_config=linux_config,
                                                                os_virtual_hard_disk=os_hd,
                                                                network_config=network_config,
                                                                availability_set_name=None,
                                                                data_virtual_hard_disks=None,
                                                                role_size=self.config.getBuildInstanceRoleSize())
            self._wait_for_operation_success(result.request_id, timeout=self.config.getAzureOperationTimeout())
            self._wait_for_role_instance_status(vm_hostname, service_name, 'ReadyRole',
                                                self.config.getAzureOperationTimeout())
            logger.info("Role instance %s has been created.", vm_hostname)

    def _deleteStorageAccount(self, name):
        """
        Deletes the storage account for the web site.
        """
        logger.info("Attempting to delete storage account %s.", name)
        if self._resource_exists(lambda: self.sms.get_storage_account_properties(name)) == False:
            logger.warn("Storage account %s not found: nothing to delete.", name)
        else:
            self.sms.delete_storage_account(name)
            logger.info("Storage account %s deleted.", name)

    def _deleteService(self, name):
        """
        Deletes the specified cloud service.
        """
        logger.info("Attempting to delete cloud service %s.", name)
        if self._resource_exists(lambda: self.sms.get_hosted_service_properties(name)) == False:
            logger.warn("Cloud service %s not found: nothing to delete.", name)
        else:
            self.sms.delete_hosted_service(name)
            logger.info("Cloud service %s deleted.", name)

    def _deleteAffinityGroup(self):
        """
        Deletes the affinity group for the web site.
        """
        name = self.config.getAffinityGroupName()
        logger.info("Attempting to delete affinity group %s.", name)
        if self._resource_exists(lambda: self.sms.get_affinity_group_properties(name)) == False:
            logger.warn("Affinity group %s not found: nothing to delete.", name)
        else:
            self.sms.delete_affinity_group(name)
            logger.info("Affinity group %s deleted.", name)

    def _ensureServiceBusNamespaceExists(self):
        """
        Creates the Azure Service Bus Namespace if it does not exist.
        """
        name = self.config.getServiceBusNamespace()
        logger.info("Checking for existence of service bus namespace (name=%s).", name)
        if self._resource_exists(lambda: self.sbms.get_namespace(name)):
            logger.warn("A namespace named %s already exists.", name)
        else:
            self.sbms.create_namespace(name, self.config.getServiceLocation())
            self._wait_for_namespace_active(name)
            logger.info("Created namespace %s.", name)

    def _ensureServiceBusQueuesExist(self):
        """
        Creates Azure service bus queues required by the service.
        """
        logger.info("Checking for existence of Service Bus Queues.")
        namespace = self.sbms.get_namespace(self.config.getServiceBusNamespace())
        sbs = ServiceBusService(namespace.name, namespace.default_key, issuer='owner')
        queue_names = ['jobresponsequeue', 'windowscomputequeue', 'linuxcomputequeue']
        for name in queue_names:
            logger.info("Checking for existence of Queue %s.", name)
            sbs.create_queue(name, fail_on_exist=False)
            logger.info("Queue %s is ready.", name)

    def _deleteServiceBusNamespace(self):
        """
        Deletes the Azure Service Bus Namespace.
        """
        name = self.config.getServiceBusNamespace()
        logger.info("Attempting to delete service bus namespace %s.", name)
        if self._resource_exists(lambda: self.sbms.get_namespace(name)) == False:
            logger.warn("Namespace %s not found: nothing to delete.", name)
        else:
            self.sbms.delete_namespace(name)
            logger.info("Namespace %s deleted.", name)

    def Deploy(self, assets):
        """
        Creates a deployment.

        assets: The set of assets to create. The full set is: {'build', 'web'}.
        """
        if len(assets) == 0:
            raise ValueError("Set of assets to deploy is not specified.")
        logger.info("Starting deployment operation.")
        self._ensureAffinityGroupExists()
        self._ensureStorageAccountExists(self.config.getStorageAccountName())
        ## Build instance
        if 'build' in assets:
            self._ensureServiceExists(self.config.getBuildServiceName(), self.config.getAffinityGroupName())
            self._ensureServiceCertificateExists(self.config.getBuildServiceName())
            self._ensureBuildMachineExists()
        # Web instances
        if 'web' in assets:
            self._ensureStorageAccountExists(self.config.getServiceStorageAccountName())
            self._ensureStorageContainersExist()
            self.ensureStorageHasCorsConfiguration()
            self._ensureServiceBusNamespaceExists()
            self._ensureServiceBusQueuesExist()
            self._ensureServiceExists(self.config.getServiceName(), self.config.getAffinityGroupName())
            self._ensureServiceCertificateExists(self.config.getServiceName())
            self._ensureVirtualMachinesExist()
        #queues
        logger.info("Deployment operation is complete.")

    def Teardown(self, assets):
        """
        Deletes a deployment.

        assets: The set of assets to delete. The full set is: {'web', 'build'}.
        """
        if len(assets) == 0:
            raise ValueError("Set of assets to teardown is not specified.")
        logger.info("Starting teardown operation.")
        if 'web' in assets:
            self._deleteVirtualMachines(self.config.getServiceName())
            self._deleteService(self.config.getServiceName())
            self._deleteStorageAccount(self.config.getServiceStorageAccountName())
        if 'build' in assets:
            self._deleteVirtualMachines(self.config.getBuildServiceName())
            self._deleteService(self.config.getBuildServiceName())
            self._deleteStorageAccount(self.config.getStorageAccountName())
        if ('web' in assets) and ('build' in assets):
            self._deleteServiceBusNamespace()
            self._deleteAffinityGroup()
        logger.info("Teardown operation is complete.")

    def getSettingsFileContent(self):
        """
        Generates the content of the local Django settings file.
        """
        allowed_hosts = ['{0}.cloudapp.net'.format(self.config.getServiceName())]
        allowed_hosts.extend(self.config.getWebHostnames())
        allowed_hosts.extend(['www.codalab.org', 'codalab.org'])
        ssl_allowed_hosts = self.config.getSslRewriteHosts();
        if len(ssl_allowed_hosts) == 0:
            ssl_allowed_hosts = allowed_hosts

        storage_key = self._getStorageAccountKey(self.config.getServiceStorageAccountName())
        namespace = self.sbms.get_namespace(self.config.getServiceBusNamespace())

        if len(self.config.getSslCertificateInstalledPath()) > 0:
            bundle_auth_scheme = "https"
        else:
            bundle_auth_scheme = "http"
        if len(ssl_allowed_hosts) == 0:
            bundle_auth_host = '{0}.cloudapp.net'.format(self.config.getServiceName())
        else:
            bundle_auth_host = ssl_allowed_hosts[0]
        bundle_auth_url = "{0}://{1}".format(bundle_auth_scheme, bundle_auth_host)

        lines = [
            "from base import Base",
            "from default import *",
            "from configurations import Settings",
            "",
            "import sys",
            "from os.path import dirname, abspath, join",
            "from pkgutil import extend_path",
            "import codalab",
            "",
            "class {0}(Base):".format(self.config.getDjangoConfiguration()),
            "",
            "    DEBUG=False",
            "",
            "    ALLOWED_HOSTS = {0}".format(allowed_hosts),
            "",
            "    SSL_PORT = '443'",
            "    SSL_CERTIFICATE = '{0}'".format(self.config.getSslCertificateInstalledPath()),
            "    SSL_CERTIFICATE_KEY = '{0}'".format(self.config.getSslCertificateKeyInstalledPath()),
            "    SSL_ALLOWED_HOSTS = {0}".format(ssl_allowed_hosts),
            "",
            "    DEFAULT_FILE_STORAGE = 'codalab.azure_storage.AzureStorage'",
            "    AZURE_ACCOUNT_NAME = '{0}'".format(self.config.getServiceStorageAccountName()),
            "    AZURE_ACCOUNT_KEY = '{0}'".format(storage_key),
            "    AZURE_CONTAINER = '{0}'".format(self.config.getServicePublicStorageContainer()),
            "    BUNDLE_AZURE_ACCOUNT_NAME = AZURE_ACCOUNT_NAME",
            "    BUNDLE_AZURE_ACCOUNT_KEY = AZURE_ACCOUNT_KEY",
            "    BUNDLE_AZURE_CONTAINER = '{0}'".format(self.config.getServiceBundleStorageContainer()),
            "",
            "    SBS_NAMESPACE = '{0}'".format(self.config.getServiceBusNamespace()),
            "    SBS_ISSUER = 'owner'",
            "    SBS_ACCOUNT_KEY = '{0}'".format(namespace.default_key),
            "    SBS_RESPONSE_QUEUE = 'jobresponsequeue'",
            "    SBS_COMPUTE_QUEUE = 'windowscomputequeue'",
            "",
            "    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'",
            "    EMAIL_HOST = '{0}'".format(self.config.getEmailHost()),
            "    EMAIL_HOST_USER = '******'".format(self.config.getEmailUser()),
            "    EMAIL_HOST_PASSWORD = '******'".format(self.config.getEmailPassword()),
            "    EMAIL_PORT = 587",
            "    EMAIL_USE_TLS = True",
            "    DEFAULT_FROM_EMAIL = 'CodaLab <*****@*****.**>'",
            "    SERVER_EMAIL = '*****@*****.**'",
            "",
            "    # Django secret",
            "    SECRET_KEY = '{0}'".format(self.config.getDjangoSecretKey()),
            "",
            "    ADMINS = (('CodaLab', '*****@*****.**'),)",
            "    MANAGERS = ADMINS",
            "",
            "    DATABASES = {",
            "        'default': {",
            "            'ENGINE': '{0}',".format(self.config.getDatabaseEngine()),
            "            'NAME': '{0}',".format(self.config.getDatabaseName()),
            "            'USER': '******',".format(self.config.getDatabaseUser()),
            "            'PASSWORD': '******',".format(self.config.getDatabasePassword()),
            "            'HOST': '{0}',".format(self.config.getDatabaseHost()),
            "            'PORT': '{0}', ".format(self.config.getDatabasePort()),
            "            'OPTIONS' : {",
            "                'init_command': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED',",
            "                'read_timeout': 5",
            "            }",
            "        }",
            "    }",
            "",
            "    BUNDLE_DB_NAME = '{0}'".format(self.config.getBundleServiceDatabaseName()),
            "    BUNDLE_DB_USER = '******'".format(self.config.getBundleServiceDatabaseUser()),
            "    BUNDLE_DB_PASSWORD = '******'".format(self.config.getBundleServiceDatabasePassword()),
            "    BUNDLE_APP_ID = '{0}'".format(self.config.getBundleServiceAppId()),
            "    BUNDLE_APP_KEY = '{0}'".format(self.config.getBundleServiceAppKey()),
            "    BUNDLE_AUTH_URL = '{0}'".format(bundle_auth_url),
            "",
            "    BUNDLE_SERVICE_URL = '{0}'".format(self.config.getBundleServiceUrl()),
            "    BUNDLE_SERVICE_CODE_PATH = '/home/{0}/deploy/bundles'".format(self.config.getVirtualMachineLogonUsername()),
            "    sys.path.append(BUNDLE_SERVICE_CODE_PATH)",
            "    codalab.__path__ = extend_path(codalab.__path__, codalab.__name__)",
            "",
        ]
        preview = self.config.getShowPreviewFeatures()
        if preview >= 1:
            if preview == 1:
                lines.append("    PREVIEW_WORKSHEETS = True")
            if preview > 1:
                lines.append("    SHOW_BETA_FEATURES = True")
            lines.append("")
        return '\n'.join(lines)
class AzureServicesManager:
    # Storage
    container = 'vhds'
    windows_blob_url = 'blob.core.windows.net'

    # Linux
    linux_user = '******'
    linux_pass = '******'
    location = 'West US'

    # SSH Keys

    def __init__(self, subscription_id, cert_file):
        self.subscription_id = subscription_id
        self.cert_file = cert_file
        self.sms = ServiceManagementService(self.subscription_id,
                                            self.cert_file)

    @property
    def sms(self):
        return self.sms

    def list_locations(self):
        locations = self.sms.list_locations()
        for location in locations:
            print location

    def list_images(self):
        return self.sms.list_os_images()

    @utils.resource_not_found_handler
    def get_hosted_service(self, service_name):
        resp = self.sms.get_hosted_service_properties(service_name)
        properties = resp.hosted_service_properties
        return properties.__dict__

    def delete_hosted_service(self, service_name):
        res = self.sms.check_hosted_service_name_availability(service_name)
        if not res.result:
            return

        self.sms.delete_hosted_service(service_name)

    def create_hosted_service(self, os_user, service_name=None, random=False):
        if not service_name:
            service_name = self.generate_cloud_service_name(os_user, random)

        available = False

        while not available:
            res = self.sms.check_hosted_service_name_availability(service_name)
            if not res.result:
                service_name = self.generate_cloud_service_name(
                    os_user, random)
            else:
                available = True

        self.sms.create_hosted_service(service_name=service_name,
                                       label=service_name,
                                       location='West US')

        return service_name

    def create_virtual_machine(self, service_name, vm_name, image_name,
                               role_size):
        media_link = self._get_media_link(vm_name)
        # Linux VM configuration
        hostname = '-'.join((vm_name, 'host'))
        linux_config = LinuxConfigurationSet(hostname, self.linux_user,
                                             self.linux_pass, True)

        # Hard disk for the OS
        os_hd = OSVirtualHardDisk(image_name, media_link)

        # Create vm
        result = self.sms.create_virtual_machine_deployment(
            service_name=service_name,
            deployment_name=vm_name,
            deployment_slot='production',
            label=vm_name,
            role_name=vm_name,
            system_config=linux_config,
            os_virtual_hard_disk=os_hd,
            role_size=role_size)
        request_id = result.request_id

        return {'request_id': request_id, 'media_link': media_link}

    def delete_virtual_machine(self, service_name, vm_name):
        resp = self.sms.delete_deployment(service_name, vm_name, True)
        self.sms.wait_for_operation_status(resp.request_id)
        result = self.sms.delete_hosted_service(service_name)
        return result

    def generate_cloud_service_name(self, os_user=None, random=False):
        if random:
            return utils.generate_random_name(10)

        return '-'.join((os_user, utils.generate_random_name(6)))

    @utils.resource_not_found_handler
    def get_virtual_machine_info(self, service_name, vm_name):
        vm_info = {}
        deploy_info = self.sms.get_deployment_by_name(service_name, vm_name)

        if deploy_info and deploy_info.role_instance_list:
            vm_info = deploy_info.role_instance_list[0].__dict__

        return vm_info

    def list_virtual_machines(self):
        vm_list = []
        services = self.sms.list_hosted_services()
        for service in services:
            deploys = service.deployments
            if deploys and deploys.role_instance_list:
                vm_name = deploys.role_instance_list[0].instance_name
                vm_list.append(vm_name)

        return vm_list

    def power_on(self, service_name, vm_name):
        resp = self.sms.start_role(service_name, vm_name, vm_name)
        return resp.request_id

    def power_off(self, service_name, vm_name):
        resp = self.sms.shutdown_role(service_name, vm_name, vm_name)
        return resp.request_id

    def soft_reboot(self, service_name, vm_name):
        resp = self.sms.restart_role(service_name, vm_name, vm_name)
        return resp.request_id

    def hard_reboot(self, service_name, vm_name):
        resp = self.sms.reboot_role_instance(service_name, vm_name, vm_name)
        return resp.request_id

    def attach_volume(self, service_name, vm_name, size, lun):
        disk_name = utils.generate_random_name(5, vm_name)
        media_link = self._get_media_link(vm_name, disk_name)

        self.sms.add_data_disk(service_name,
                               vm_name,
                               vm_name,
                               lun,
                               host_caching='ReadWrite',
                               media_link=media_link,
                               disk_name=disk_name,
                               logical_disk_size_in_gb=size)

    def detach_volume(self, service_name, vm_name, lun):
        self.sms.delete_data_disk(service_name, vm_name, vm_name, lun, True)

    def get_available_lun(self, service_name, vm_name):
        try:
            role = self.sms.get_role(service_name, vm_name, vm_name)
        except Exception:
            return 0

        disks = role.data_virtual_hard_disks
        luns = [disk.lun for disk in disks].sort()

        for i in range(1, 16):
            if i not in luns:
                return i

        return None

    def snapshot(self, service_name, vm_name, image_id, snanshot_name):
        image_desc = 'Snapshot for image %s' % vm_name
        image = CaptureRoleAsVMImage('Specialized', snanshot_name, image_id,
                                     image_desc, 'english')

        resp = self.sms.capture_vm_image(service_name, vm_name, vm_name, image)

        self.sms.wait_for_operation_status(resp.request_id)

    def _get_media_link(self, vm_name, filename=None, storage_account=None):
        """ The MediaLink should be constructed as:
        https://<storageAccount>.<blobLink>/<blobContainer>/<filename>.vhd
        """
        if not storage_account:
            storage_account = self._get_or_create_storage_account()

        container = self.container
        filename = vm_name if filename is None else filename
        blob = vm_name + '-' + filename + '.vhd'
        media_link = "http://%s.%s/%s/%s" % (
            storage_account, self.windows_blob_url, container, blob)

        return media_link

    def _get_or_create_storage_account(self):
        account_list = self.sms.list_storage_accounts()

        if account_list:
            return account_list[-1].service_name

        storage_account = utils.generate_random_name(10)
        description = "Storage account %s description" % storage_account
        label = storage_account + 'label'
        self.sms.create_storage_account(storage_account,
                                        description,
                                        label,
                                        location=self.location)

        return storage_account

    def _wait_for_operation(self,
                            request_id,
                            timeout=3000,
                            failure_callback=None,
                            failure_callback_kwargs=None):
        try:
            self.sms.wait_for_operation_status(request_id, timeout=timeout)
        except Exception as ex:
            if failure_callback and failure_callback_kwargs:
                failure_callback(**failure_callback_kwargs)

            raise ex
예제 #7
0
class AffinityGroupManagementServiceTest(AzureTestCase):
    def setUp(self):
        proxy_host = credentials.getProxyHost()
        proxy_port = credentials.getProxyPort()

        self.sms = ServiceManagementService(
            credentials.getSubscriptionId(),
            credentials.getManagementCertFile())
        if proxy_host:
            self.sms.set_proxy(proxy_host, proxy_port)

        self.affinity_group_name = getUniqueNameBasedOnCurrentTime('utaffgrp')
        self.hosted_service_name = None
        self.storage_account_name = None

    def tearDown(self):
        try:
            if self.hosted_service_name is not None:
                self.sms.delete_hosted_service(self.hosted_service_name)
        except:
            pass

        try:
            if self.storage_account_name is not None:
                self.sms.delete_storage_account(self.storage_account_name)
        except:
            pass

        try:
            self.sms.delete_affinity_group(self.affinity_group_name)
        except:
            pass

    #--Helpers-----------------------------------------------------------------
    def _create_affinity_group(self, name):
        result = self.sms.create_affinity_group(name, 'tstmgmtaffgrp',
                                                'West US',
                                                'tstmgmt affinity group')
        self.assertIsNone(result)

    def _affinity_group_exists(self, name):
        try:
            props = self.sms.get_affinity_group_properties(name)
            return props is not None
        except:
            return False

    #--Test cases for affinity groups ------------------------------------
    def test_list_affinity_groups(self):
        # Arrange
        self._create_affinity_group(self.affinity_group_name)

        # Act
        result = self.sms.list_affinity_groups()

        # Assert
        self.assertIsNotNone(result)
        self.assertTrue(len(result) > 0)

        group = None
        for temp in result:
            if temp.name == self.affinity_group_name:
                group = temp
                break

        self.assertIsNotNone(group)
        self.assertIsNotNone(group.name)
        self.assertIsNotNone(group.label)
        self.assertIsNotNone(group.description)
        self.assertIsNotNone(group.location)
        self.assertIsNotNone(group.capabilities)
        self.assertTrue(len(group.capabilities) > 0)

    def test_get_affinity_group_properties(self):
        # Arrange
        self.hosted_service_name = getUniqueNameBasedOnCurrentTime('utsvc')
        self.storage_account_name = getUniqueNameBasedOnCurrentTime(
            'utstorage')
        self._create_affinity_group(self.affinity_group_name)
        self.sms.create_hosted_service(self.hosted_service_name,
                                       'affgrptestlabel', 'affgrptestdesc',
                                       None, self.affinity_group_name)
        self.sms.create_storage_account(self.storage_account_name,
                                        self.storage_account_name + 'desc',
                                        self.storage_account_name + 'label',
                                        self.affinity_group_name)

        # Act
        result = self.sms.get_affinity_group_properties(
            self.affinity_group_name)

        # Assert
        self.assertIsNotNone(result)
        self.assertEqual(result.name, self.affinity_group_name)
        self.assertIsNotNone(result.label)
        self.assertIsNotNone(result.description)
        self.assertIsNotNone(result.location)
        self.assertIsNotNone(result.hosted_services[0])
        self.assertEqual(result.hosted_services[0].service_name,
                         self.hosted_service_name)
        self.assertEqual(
            result.hosted_services[0].hosted_service_properties.affinity_group,
            self.affinity_group_name)
        # not sure why azure does not return any storage service
        self.assertTrue(len(result.capabilities) > 0)

    def test_create_affinity_group(self):
        # Arrange
        label = 'tstmgmtaffgrp'
        description = 'tstmgmt affinity group'

        # Act
        result = self.sms.create_affinity_group(self.affinity_group_name,
                                                label, 'West US', description)

        # Assert
        self.assertIsNone(result)
        self.assertTrue(self._affinity_group_exists(self.affinity_group_name))

    def test_update_affinity_group(self):
        # Arrange
        self._create_affinity_group(self.affinity_group_name)
        label = 'tstlabelupdate'
        description = 'testmgmt affinity group update'

        # Act
        result = self.sms.update_affinity_group(self.affinity_group_name,
                                                label, description)

        # Assert
        self.assertIsNone(result)
        props = self.sms.get_affinity_group_properties(
            self.affinity_group_name)
        self.assertEqual(props.label, label)
        self.assertEqual(props.description, description)

    def test_delete_affinity_group(self):
        # Arrange
        self._create_affinity_group(self.affinity_group_name)

        # Act
        result = self.sms.delete_affinity_group(self.affinity_group_name)

        # Assert
        self.assertIsNone(result)
        self.assertFalse(self._affinity_group_exists(self.affinity_group_name))

    #--Test cases for locations ------------------------------------------
    def test_list_locations(self):
        # Arrange

        # Act
        result = self.sms.list_locations()

        # Assert
        self.assertIsNotNone(result)
        self.assertTrue(len(result) > 0)
        self.assertIsNotNone(result[0].name)
        self.assertIsNotNone(result[0].display_name)
        self.assertIsNotNone(result[0].available_services)
        self.assertTrue(len(result[0].available_services) > 0)