Beispiel #1
0
    def delete(self, name):
        '''
            Deletes an existing resource

        :param string name: Name of the existing resource
        :raises: :exc:`cloudify.exceptions.RecoverableError`,
                 :exc:`cloudify.exceptions.NonRecoverableError`,
                 :exc:`requests.RequestException`
        '''
        self.log.info('Deleting {0} "{1}"'.format(self.name, name))
        if self.ctx.instance._modifiable:
            self.ctx.instance.runtime_properties['async_op'] = None
        # Make the request
        res = self.client.request(
            method='delete',
            url='{0}/{1}'.format(self.endpoint, name))
        # Convert headers from CaseInsensitiveDict to Dict
        headers = dict(res.headers)
        self.log.debug('headers: {0}'.format(
            utils.secure_logging_content(headers)))
        # HTTP 200 (OK) - The operation is successful and complete
        if res.status_code == httplib.OK:
            return
        # HTTP 204 (NO_CONTENT) - The resource doesn't exist
        elif res.status_code == httplib.NO_CONTENT:
            return
        # HTTP 202 (ACCEPTED) - The operation has started but is asynchronous
        elif res.status_code == httplib.ACCEPTED:
            if not headers.get('location'):
                raise RecoverableError(
                    'HTTP 202 ACCEPTED but no Location header present')
            if not self.ctx.instance._modifiable:
                self.log.warn(
                    'Not retrying async operation, '
                    'because NodeInstanceContext is not modifiable. '
                    'headers: {0}'.format(headers)
                )
                return
            self.ctx.instance.runtime_properties['async_op'] = headers
            return ctx.operation.retry(
                'Operation: "{0}" started'
                .format(self.get_operation_id(headers)),
                retry_after=self.get_retry_after(headers))
        # HTTP 400 (BAD_REQUEST) - We're sending bad data
        elif res.status_code == httplib.BAD_REQUEST:
            self.log.info('BAD REQUEST: response: {}'.format(
                utils.secure_logging_content(res.content)))
            raise UnexpectedResponse(
                'Recieved HTTP 400 BAD REQUEST', res.json())
        # HTTP 409 (CONFLICT) - Operation failed
        elif res.status_code == httplib.CONFLICT:
            raise NonRecoverableError(
                'Operation failed. (code={0}, data={1})'
                .format(res.status_code, res.text))
        # All other errors will be treated as recoverable
        elif res.status_code != httplib.CREATED:
            raise RecoverableError(
                'Expected HTTP status code {0}, recieved {1}'
                .format(httplib.CREATED, res.status_code))
Beispiel #2
0
 def get(self, group_name, vm_name):
     self.logger.info("Get virtual_machine...{0}".format(vm_name))
     virtual_machine = self.client.virtual_machines.get(
         resource_group_name=group_name, vm_name=vm_name).as_dict()
     self.logger.info('Get virtual_machine result: {0}'.format(
         utils.secure_logging_content(virtual_machine)))
     return virtual_machine
Beispiel #3
0
 def get(self, group_name, plan_name):
     self.logger.info("Get plan...{0}".format(plan_name))
     plan = self.client.app_service_plans.get(
         resource_group_name=group_name, name=plan_name).as_dict()
     self.logger.info('Get plan result: {0}'.format(
         utils.secure_logging_content(plan)))
     return plan
Beispiel #4
0
 def get(self, group_name, resource_name):
     self.logger.info("Get managed_cluster...{0}".format(resource_name))
     managed_cluster = self.client.managed_clusters.get(
         resource_group_name=group_name,
         resource_name=resource_name).as_dict()
     self.logger.info('Get managed_cluster result: {0}'.format(
         utils.secure_logging_content(managed_cluster)))
     return managed_cluster
Beispiel #5
0
    def operation_complete(self, op_info):
        '''
            Checks the status of an asynchronous operation

        :param dict op_info: Long-running operation headers
        :raises: :exc:`cloudify.exceptions.RecoverableError`,
                 :exc:`cloudify.exceptions.NonRecoverableError`,
                 :exc:`requests.RequestException`
        '''
        # Get the operation ID
        op_id = op_info.get('x-ms-request-id', 'not-reported')
        # Make the request
        self.log.info('Checking status of operation "{0}"'
                      .format(op_id))
        op_url = op_info.get('location') or \
            op_info.get('azure-asyncoperation')
        res = self.client.request(method='get',
                                  url=op_url)
        # Convert headers from CaseInsensitiveDict to Dict
        headers = dict(res.headers)
        self.log.debug('headers: {0}'.format(
            utils.secure_logging_content(headers)))
        # HTTP 200 (OK) - Operation is successful and complete
        if res.status_code == httplib.OK:
            if self.validate_res_json(res) == 'InProgress':
                return ctx.operation.retry(
                    'Operation: "{0}" still pending'
                    .format(self.get_operation_id(headers)),
                    retry_after=self.get_retry_after(headers))
            self.ctx.instance.runtime_properties['async_op'] = None
            return
        # Clear the async state
        self.ctx.instance.runtime_properties['async_op'] = None
        # HTTP 202 (ACCEPTED) - Operation is still pending
        if res.status_code == httplib.ACCEPTED:
            if not headers.get('location'):
                raise RecoverableError(
                    'HTTP 202 ACCEPTED but no Location header present')
            self.ctx.instance.runtime_properties['async_op'] = headers
            return ctx.operation.retry(
                'Operation: "{0}" still pending'
                .format(self.get_operation_id(headers)),
                retry_after=self.get_retry_after(headers))
        # HTTP 409 (CONFLICT) - Operation failed
        elif res.status_code == httplib.CONFLICT:
            raise NonRecoverableError(
                'Operation "{0}" failed. (code={1}, data={2})'
                .format(op_id, res.status_code, res.text))
        # HTTP 500 (INTERNAL_SERVER_ERROR) - Operation time out
        elif res.status_code == httplib.INTERNAL_SERVER_ERROR:
            # Preserve op_info in case it's really just a time-out
            self.ctx.instance.runtime_properties['async_op'] = op_info
            return ctx.operation.retry(
                'Operation: "{0}" still pending'
                .format(self.get_operation_id(headers)),
                retry_after=self.get_retry_after(headers))
        res.raise_for_status()
Beispiel #6
0
 def get(self, group_name, network_interface_name):
     self.logger.info(
         "Get network_interface...{0}".format(network_interface_name))
     network_interface = self.client.network_interfaces.get(
         resource_group_name=group_name,
         network_interface_name=network_interface_name).as_dict()
     self.logger.info('Get network_interface result: {0}'.format(
         utils.secure_logging_content(network_interface)))
     return network_interface
Beispiel #7
0
 def get(self, group_name, virtual_network_name):
     self.logger.info(
         "Get virtual_network...{0}".format(virtual_network_name))
     virtual_network = self.client.virtual_networks.get(
         resource_group_name=group_name,
         virtual_network_name=virtual_network_name).as_dict()
     self.logger.info('Get virtual_network result: {0}'.format(
         utils.secure_logging_content(virtual_network)))
     return virtual_network
 def get(self, group_name, container_service_name):
     self.logger.info(
         "Get container_service...{0}".format(container_service_name))
     container_service = self.client.container_services.get(
         resource_group_name=group_name,
         container_service_name=container_service_name).as_dict()
     self.logger.info('Get container_service result: {0}'.format(
         utils.secure_logging_content(container_service)))
     return container_service
Beispiel #9
0
    def get(self, name=None):
        '''
            Gets details about an existing resource

        :param string name: Name of the existing resource
        :returns: Response data from the Azure API call
        :rtype: dict
        :raises: :exc:`cloudify.exceptions.RecoverableError`,
                 :exc:`cloudify.exceptions.NonRecoverableError`,
                 :exc:`requests.RequestException`
        '''
        self.log.info('Retrieving {0} "{1}"'.format(self.name, name))
        # Make the request
        if name:
            url = '{0}/{1}'.format(self.endpoint, name)
        else:
            url = self.endpoint
        res = self.client.request(
            method='get',
            url=url)
        # Convert headers from CaseInsensitiveDict to Dict
        headers = dict(res.headers)
        self.log.debug('headers: {0}'.format(
            utils.secure_logging_content(headers)))
        # Check the response
        # HTTP 202 (ACCEPTED) - The operation has started but is asynchronous
        if res.status_code == httplib.ACCEPTED:
            if not headers.get('location'):
                raise RecoverableError(
                    'HTTP 202 ACCEPTED but no Location header present')
            self.ctx.instance.runtime_properties['async_op'] = headers
            return ctx.operation.retry(
                'Operation: "{0}" started'
                .format(self.get_operation_id(headers)),
                retry_after=self.get_retry_after(headers))
        # HTTP 200 (OK) - The resource already exists
        elif res.status_code == httplib.OK:
            return res.json()
        # If Azure sent a 400, we're sending bad data
        if res.status_code == httplib.BAD_REQUEST:
            self.log.info('BAD REQUEST: response: {}'.format(res.content))
            raise UnexpectedResponse(
                'Recieved HTTP 400 BAD REQUEST', res.json())
        # If Azure sent a 404, the resource doesn't exist (yet?)
        if res.status_code == httplib.NOT_FOUND:
            raise RecoverableError(
                '{0} "{1}" doesn\'t exist (yet?)'
                .format(self.name, name))
        # All other errors will be treated as recoverable
        if res.status_code != httplib.CREATED:
            raise RecoverableError(
                'Expected HTTP status code {0}, recieved {1}'
                .format(httplib.CREATED, res.status_code))
        return res.json()
Beispiel #10
0
 def create_or_update(self, group_name, plan_name, params):
     self.logger.info("Create/Updating plan...{0}".format(plan_name))
     create_async_operation = \
         self.client.app_service_plans.create_or_update(
             resource_group_name=group_name,
             name=plan_name,
             app_service_plan=params,
         )
     create_async_operation.wait()
     plan = create_async_operation.result().as_dict()
     self.logger.info('Create plan result: {0}'.format(
         utils.secure_logging_content(plan)))
     return plan
Beispiel #11
0
 def create_or_update(self, group_name, virtual_network_name, params):
     self.logger.info("Create/Updating virtual_network...{0}".format(
         virtual_network_name))
     async_vnet_creation = self.client.virtual_networks.create_or_update(
         resource_group_name=group_name,
         virtual_network_name=virtual_network_name,
         parameters=params,
     )
     async_vnet_creation.wait()
     virtual_network = async_vnet_creation.result().as_dict()
     self.logger.info('create virtual_network result: {0}'.format(
         utils.secure_logging_content(virtual_network)))
     return virtual_network
Beispiel #12
0
 def create_or_update(self, group_name, vm_name, params):
     self.logger.info(
         "Create/Updating virtual_machine...{0}".format(vm_name))
     create_async_operation = self.client.virtual_machines.create_or_update(
         resource_group_name=group_name,
         vm_name=vm_name,
         parameters=params,
     )
     create_async_operation.wait()
     virtual_machine = create_async_operation.result().as_dict()
     self.logger.info('Create virtual_machine result: {0}'.format(
         utils.secure_logging_content(virtual_machine)))
     return virtual_machine
Beispiel #13
0
    def exists(self, name=None):
        '''
            Determines if a resource exists or not

        :param string name: Name of the existing resource
        :returns: True if resource exists, False if it doesn't
        :rtype: boolean
        :raises: :exc:`cloudify_azure.exceptions.UnexpectedResponse`,
                 :exc:`requests.RequestException`
        '''
        self.log.info('Checking {0} "{1}"'.format(self.name, name))
        # Make the request
        if name:
            url = '{0}/{1}'.format(self.endpoint, name)
        else:
            url = self.endpoint
        res = self.client.request(
            method='get',
            url=url)
        # Convert headers from CaseInsensitiveDict to Dict
        headers = dict(res.headers)
        self.log.debug('headers: {0}'.format(
            utils.secure_logging_content(headers)))
        # Check the response
        # HTTP 202 (ACCEPTED) - An asynchronous operation has started
        if res.status_code == httplib.ACCEPTED:
            return True
        # HTTP 200 (OK) - The resource already exists
        elif res.status_code == httplib.OK:
            return True
        # HTTP 404 (NOT_FOUND) - The resource can't be found
        elif res.status_code == httplib.NOT_FOUND:
            return False
        # HTTP 400 (BAD_REQUEST) - The resource name is invalid
        elif res.status_code == httplib.BAD_REQUEST:
            raise ValueError('Invalid resource name')
        raise UnexpectedResponse(
            'Recieved unexpected HTTP ({0}) response'
            .format(res.status_code), res.json())
Beispiel #14
0
    def update(self, name, params, force=False):
        '''
            Updates an existing resource

        :param string name: Name of the resource
        :param dict params: Parameters to update the resource with
        :param boolean force: Forces the params to be sent without merging
            with the resources' existing data
        :raises: :exc:`cloudify.exceptions.RecoverableError`,
                 :exc:`cloudify.exceptions.NonRecoverableError`,
                 :exc:`requests.RequestException`
        '''
        if not force:
            # Get the existing data (since partial updates seem to
            # be in a questionable state on Azure's side of things)
            data = self.get(name)
            # Updating the data with our new data
            params = utils.dict_update(data, params)
        self.log.info('Updating {0} "{1}"'.format(self.name, name))
        if self.ctx.instance._modifiable:
            self.ctx.instance.runtime_properties['async_op'] = None
        # Sanitize input data
        params = self.sanitize_json_input(params)
        # Make the request
        res = self.client.request(
            method='put',
            url='{0}/{1}'.format(self.endpoint, name),
            json=params)
        # Convert headers from CaseInsensitiveDict to Dict
        headers = dict(res.headers)
        self.log.debug('headers: {0}'.format(
            utils.secure_logging_content(headers)))
        # Check the response
        # HTTP 202 (ACCEPTED) - The operation has started but is asynchronous
        if res.status_code == httplib.ACCEPTED:
            if not headers.get('location'):
                raise RecoverableError(
                    'HTTP 202 ACCEPTED but no Location header present')
            if not self.ctx.instance._modifiable:
                self.log.warn(
                    'Not retrying async operation, '
                    'because NodeInstanceContext is not modifiable. '
                    'headers: {0}'.format(headers)
                )
                return
            self.ctx.instance.runtime_properties['async_op'] = headers
            return ctx.operation.retry(
                'Operation: "{0}" started'
                .format(self.get_operation_id(headers)),
                retry_after=self.get_retry_after(headers))
        # HTTP 200 (OK) - The resource already exists
        elif res.status_code == httplib.OK:
            if headers.get('azure-asyncoperation'):
                if not self.ctx.instance._modifiable:
                    self.log.warn(
                        'Not retrying async operation, '
                        'because NodeInstanceContext is not modifiable. '
                        'headers: {0}'.format(headers)
                    )
                    return
                self.ctx.instance.runtime_properties['async_op'] = headers
                return ctx.operation.retry(
                    'Operation: "{0}" started'
                    .format(self.get_operation_id(headers)),
                    retry_after=self.get_retry_after(headers))
            self.log.warn('{0} already exists. Using resource.'
                          .format(self.name))
            return
        # HTTP 400 (BAD_REQUEST) - We're sending bad data
        elif res.status_code == httplib.BAD_REQUEST:
            self.log.info('BAD REQUEST: response: {}'.format(
                utils.secure_logging_content(res.content)))
            raise UnexpectedResponse(
                'Recieved HTTP 400 BAD REQUEST', res.json())
        # HTTP 409 (CONFLICT) - Operation failed
        elif res.status_code == httplib.CONFLICT:
            raise NonRecoverableError(
                'Operation failed. (code={0}, data={1})'
                .format(res.status_code, res.text))
        # All other errors will be treated as recoverable
        elif res.status_code != httplib.CREATED:
            raise RecoverableError(
                'Expected HTTP status code {0}, recieved {1}'
                .format(httplib.CREATED, res.status_code))
    def request(self, **kwargs):
        '''
            Builds, and executes, a request to the
            Microsoft Azure API service.  The parameters
            are passed as-is (with one exception, see notes)
            to the underlying requests.Session.request() function.

        .. note::

           The *url* parameter should be passed in as a root path
           starting AFTER the base endpoint
           https://management.azure.com/subscriptions/{subscription-id}.
           For example, to send a request to provision a Resource Group,
           the URL parameter should be */resourcegroups/{rg-name}*.  Also,
           *api-version* request parameter will be added automatically if
           not provided.

           If a full URL is passed in (any string not starting with a
           forward slash), it will be passed as-is to the underlying
           request() function.

        :returns: A configured requests.Session instance
        :rtype: :class:`requests.Response`
        '''
        # Get current credentials
        creds = utils.get_credentials(_ctx=self.ctx)
        # Rework the URL
        url = kwargs.pop('url', '')
        # Check if this is a relative operation
        if url.startswith('/'):
            # Add the endpoint and subscription ID
            url = '{0}/subscriptions/{1}{2}'.format(
                creds.endpoints_resource_manager,
                creds.subscription_id,
                url)
        kwargs['url'] = url
        # SSL Verification
        kwargs['verify'] = creds.endpoint_verify

        # Update the params list with the api version
        url_params = urlparse.parse_qs(urlparse.urlparse(url).query)
        if not url_params.get('api-version'):
            params = kwargs.pop('params', dict())
            params['api-version'] = params.get('api-version', self.api_version)
            kwargs['params'] = params
        # Log the request details
        self.log.info('request({0})'.format(utils.secure_logging_content(
            kwargs)))
        res = self.session.request(**kwargs)
        # Only get data if there's data to be gotten
        data = None
        if res.text:
            data = res.json()
        self.log.debug('response: '
                       '(status={0}, data={1})'.format(
                           res.status_code,
                           json.dumps(data, indent=2)))
        # Check for 401 Unauthorized
        if res.status_code == 401:
            # We received an explicit credentials rejection
            if 'error' in data and data['error'] == 'invalid_client':
                raise exceptions.InvalidCredentials(
                    'Azure rejected the provided credentials', data)
            # All other rejection reason will be caught here
            raise exceptions.UnauthorizedRequest(data)
        return res