예제 #1
0
def run_subprocess(command,
                   logger,
                   cwd=None,
                   additional_env=None,
                   additional_args=None,
                   return_output=False):

    if additional_args is None:
        additional_args = {}
    if 'max_sleep_time' not in additional_args:
        additional_args['max_sleep_time'] = 299
    args_to_pass = copy.deepcopy(additional_args)
    if additional_env:
        passed_env = args_to_pass.setdefault('env', {})
        passed_env.update(os.environ)
        passed_env.update(additional_env)

    logger.info(
        "Running: command={cmd}, cwd={cwd}, additional_args={args}".format(
            cmd=obfuscate_passwords(command),
            cwd=cwd,
            args=obfuscate_passwords(args_to_pass)))

    general_executor_params = copy.deepcopy(args_to_pass)
    general_executor_params['cwd'] = cwd
    general_executor_params['log_stdout'] = return_output
    general_executor_params['log_stderr'] = True
    general_executor_params['stderr_to_stdout'] = False
    script_path = command.pop(0)
    general_executor_params['args'] = command

    return process_execution(
        general_executor,
        script_path,
        process=general_executor_params)
예제 #2
0
def _check_response(json, response, is_recoverable):
    if not is_recoverable:
        logger.debug('Check response (nonrecoverable) in json: {} by {}'
                     .format(shorted_text(obfuscate_passwords(json)),
                             repr(response)))
    else:
        logger.debug('Check response (recoverable) in json: {} by {}'
                     .format(shorted_text(obfuscate_passwords(json)),
                             repr(response)))

    if not response:
        return

    if not isinstance(response, list) and not is_recoverable:
        raise WrongTemplateDataException(
            "Response (nonrecoverable) had to be list. "
            "Type {} not supported. ".format(
                type(response)))

    if not isinstance(response, list) and is_recoverable:
        raise WrongTemplateDataException(
            "Response (recoverable) had to be list. "
            "Type {} not supported. ".format(
                type(response)))

    if isinstance(response[0], list):
        for item in response:
            _check_response(json, item, is_recoverable)
    else:
        pattern = response.pop(-1)
        for key in response:

            try:
                json = json[key]
            except (TypeError, IndexError, KeyError) as e:
                logger.debug(repr(e))
                raise ExpectationException(
                        'No key or index "{}" in json {}'.format(key, json))

        if (
            re.match(pattern, "{0}".format(json)) and
            not is_recoverable
        ):
            raise NonRecoverableResponseException(
                "Giving up... \n"
                "Response value: "
                "{} matches regexp:{} from nonrecoverable_response. ".format(
                    json, pattern))
        if (
            not re.match(pattern, "{0}".format(json)) and
            is_recoverable
        ):
            raise RecoverableResponseException(
                "Trying one more time...\n"
                "Response value:{} does not match regexp: {} "
                "from response_expectation".format(
                    json, pattern))
def execute_as_workflow(*args, **kwargs):
    # get current context
    ctx = kwargs.get('ctx', CloudifyContext)
    if ctx.type != context.DEPLOYMENT:
        raise NonRecoverableError(
            "Called with wrong context: {ctx_type}".format(ctx_type=ctx.type))

    # register logger file
    logger_file = kwargs.get('logger_file')
    if logger_file:
        fh = logging.FileHandler(logger_file)
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(
            logging.Formatter(fmt="%(asctime)s %(levelname)s %(message)s",
                              datefmt="%Y-%m-%d %H:%M:%S"))
        ctx.logger.addHandler(fh)

    # check inputs
    if len(args):
        inputs = args[0]
    else:
        inputs = kwargs.get('inputs', {})

    auth = kwargs.get('auth')
    params = kwargs.get('params', {})
    node_props = kwargs.get('properties', {})
    template_file = kwargs.get('template_file')
    save_path = kwargs.get('save_path')
    prerender = kwargs.get('prerender', False)
    remove_calls = kwargs.get('remove_calls', False)
    if not params:
        params = {}

    ctx.logger.debug("Execute as workflow params: {} template: {}".format(
        repr(obfuscate_passwords(params)), repr(template_file)))

    params['__inputs__'] = inputs

    # place for save responses
    runtime_properties = {}

    _execute(params=params,
             template_file=template_file,
             ctx=ctx,
             instance_props=runtime_properties,
             node_props=node_props,
             save_path=save_path,
             prerender=prerender,
             remove_calls=remove_calls,
             auth=auth,
             resource_callback=workflow_get_resource,
             retry_count=kwargs.get('retry_count', 1),
             retry_sleep=kwargs.get('retry_sleep', 15))
    ctx.logger.debug("Final response: {0}".format(
        repr(obfuscate_passwords(runtime_properties))))
def _update_runtime_properties(ctx, instance_id, properties_updates):
    manager = get_rest_client()

    resulted_state = manager.node_instances.get(instance_id)
    ctx.logger.debug('State before update: {}'.format(
        repr(obfuscate_passwords(resulted_state))))
    ctx.logger.info("Update node: {}".format(instance_id))
    runtime_properties = resulted_state.runtime_properties or {}
    runtime_properties.update(properties_updates)
    manager.node_instances.update(node_instance_id=instance_id,
                                  runtime_properties=runtime_properties,
                                  version=resulted_state.version + 1)
    resulted_state = manager.node_instances.get(instance_id)
    ctx.logger.debug('State after update: {}'.format(
        repr(obfuscate_passwords(resulted_state))))
 def test_obfuscate_other_secrets(self):
     call = {
         'Token': 'HIDE ME',
         'number': -2,
         'SECRET': 'HIDE ME',
         'Authentication Header': {
             u'Bearer Token': 'HIDE ME',
             u'Bearer-Token': 'HIDE ME TOO',
         },
         'message': 'Hello world!',
     }
     self.assertNotIn(u'HIDE ME',
                      u'{0}'.format(filters.obfuscate_passwords(call)))
     self.assertIn(u'Hello world!',
                   u'{0}'.format(filters.obfuscate_passwords(call)))
def _cleanup_instances(ctx, instance_ids):
    manager = get_rest_client()

    for instance_id in instance_ids:
        resulted_state = manager.node_instances.get(instance_id)
        ctx.logger.debug('State before update: {}'.format(
            repr(obfuscate_passwords(resulted_state))))
        ctx.logger.info("Cleanup node: {}".format(instance_id))
        manager.node_instances.update(node_instance_id=instance_id,
                                      runtime_properties={},
                                      state='uninitialized',
                                      version=resulted_state.version + 1)
        resulted_state = manager.node_instances.get(instance_id)
        ctx.logger.debug('State after update: {}'.format(
            repr(obfuscate_passwords(resulted_state))))
def execute(*argc, **kwargs):
    # get current context
    ctx = kwargs.get('ctx', CloudifyContext)
    auth = kwargs.get('auth')
    params = kwargs.get('params', {})
    template_file = kwargs.get('template_file')
    save_path = kwargs.get('save_path')
    prerender = kwargs.get('prerender', False)
    remove_calls = kwargs.get('remove_calls', False)
    if not params:
        params = {}

    ctx.logger.debug("Execute params: {} template: {}".format(
        repr(obfuscate_passwords(params)), repr(template_file)))
    runtime_properties = ctx.instance.runtime_properties.copy()

    runtime_properties.update(params)
    _execute(params=runtime_properties,
             template_file=template_file,
             ctx=ctx,
             instance_props=ctx.instance.runtime_properties,
             node_props=ctx.node.properties,
             save_path=save_path,
             prerender=prerender,
             remove_calls=remove_calls,
             auth=auth,
             resource_callback=ctx.get_resource,
             retry_count=kwargs.get('retry_count', 1),
             retry_sleep=kwargs.get('retry_sleep', 15))
def _run_operation(ctx, sequence, operation, **kwargs):
    ctx.logger.debug("Run {}({})".format(operation,
                                         repr(obfuscate_passwords(kwargs))))

    include_node_types = kwargs.get('include_node_types', [])
    exclude_node_types = kwargs.get('exclude_node_types', [])
    include_instances = kwargs.get('include_instances', [])

    for node in ctx.nodes:
        # check by node type
        if not _check_type(node, include_node_types, exclude_node_types):
            continue

        # check for skipped actions
        skip_actions = node.properties.get("skip_actions", [])
        if skip_actions:
            if operation in skip_actions:
                continue

        if operation in node.operations:
            for instance in node.instances:
                # check for skip instances
                if include_instances:
                    if instance.id not in include_instances:
                        continue
                # add to run operation
                sequence.add(
                    instance.send_event('Starting to {}'.format(operation)),
                    instance.execute_operation(operation,
                                               kwargs=kwargs),
                    instance.send_event('Done {}'.format(operation)))
 def test_obfuscate_passwords_dont_copy(self):
     call = {
         'host': 'localhost',
         'port': -2,
         'response_translation': {
             "object": 'object_id'
         }
     }
     self.assertIs(filters.obfuscate_passwords(call), call)
예제 #10
0
    def get_token(self):
        token = self._get_token()

        if not token:
            raise HelmKuberentesAuthenticationError(
                'Cannot generate token use {variant} for data:'
                ' {auth_data} '.format(
                    variant=self.__class__.__name__,
                    auth_data=obfuscate_passwords(self.authentication_data))
            )

        return token
    def test_obfuscate_json_string(self):
        call = """{
    "Token": "HIDE ME",
    "number": -2,
    "SECRET": "HIDE ME",
    "Authentication Header": {
        "Bearer Token": "HIDE ME",
        "Bearer-Token": "HIDE ME TOO",
    },
    "message": "Hello world!",
    "src_registry_password": {
        "value": "some_value"
    },
    "client_secret": {"secret": "b4611ohg_k0vtazwr8jn8h88rcg2a98odapqzev-"},
    "array_password": ["first_val", "second_val"],
    "true_false_token": true,
    "null_password": null,
    "set_token": ("firstToken","secondToken","thirdToken"),
    "number_secret": 123.456,
    "weird_password": "******",
    "array2_password": [123.123],
    "array3_password": [false],
    "array4_password": [true, false],
    "array5_password": [123.123,456.335,654.23],
    "dict2_password": {"x":true},
}"""
        obfuscated_call = """{
    "Token": "xxxxxxxxxxxxxxxx",
    "number": -2,
    "SECRET": "xxxxxxxxxxxxxxxx",
    "Authentication Header": {
        "Bearer Token": "xxxxxxxxxxxxxxxx",
        "Bearer-Token": "xxxxxxxxxxxxxxxx",
    },
    "message": "Hello world!",
    "src_registry_password": {
        "value": "some_value"
    },
    "client_secret": {"secret": "xxxxxxxxxxxxxxxx"},
    "array_password": ["first_val", "second_val"],
    "true_false_token": true,
    "null_password": null,
    "set_token": ("firstToken","secondToken","thirdToken"),
    "number_secret": 123.456,
    "weird_password": "******",
    "array2_password": [123.123],
    "array3_password": [false],
    "array4_password": [true, false],
    "array5_password": [123.123,456.335,654.23],
    "dict2_password": {"x":true},
}"""
        self.assertEqual(filters.obfuscate_passwords(call), obfuscated_call)
 def test_obfuscate_passwords_deep(self):
     call = {
         'host':
         'localhost',
         'port':
         -2,
         'PAssword':
         '******',
         'deeper': {
             u'password': '******'
         },
         'list': [{
             'url': 'https://example.com/',
             'auth': {
                 'username': '******',
                 'PASSWORD': '******',
             }
         }, {
             'password': '******'
         }],
         'what': {
             'should': {
                 'you': {
                     'do': {
                         'to': {
                             'go': {
                                 'this': {
                                     'deep': {
                                         'password': '******'
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         },
     }
     self.assertNotIn('HIDE ME',
                      u'{0}'.format(filters.obfuscate_passwords(call)))
예제 #13
0
def bunch_execute(templates=None, **kwargs):
    # get current context
    ctx = kwargs.get('ctx', CloudifyContext)
    auth = kwargs.get('auth')

    for template in templates or []:
        params = template.get('params', {})
        template_file = template.get('template_file')
        prerender = template.get('prerender')
        save_to = template.get('save_to')
        params_attributes = template.get('params_attributes')
        remove_calls = template.get('remove_calls')

        ctx.logger.info('Processing: {template_file}'.format(
            template_file=repr(template_file)))
        runtime_properties = {}
        if params:
            runtime_properties.update(params)
        if params_attributes:
            runtime_properties.update(
                _get_params_attributes(ctx, ctx.instance.runtime_properties,
                                       params_attributes))
        ctx.logger.debug('Params: {params}'.format(
            params=repr(obfuscate_passwords(runtime_properties))))
        runtime_properties["ctx"] = ctx
        _execute(params=runtime_properties,
                 template_file=template_file,
                 ctx=ctx,
                 instance_props=ctx.instance.runtime_properties,
                 node_props=ctx.node.properties,
                 save_path=save_to,
                 prerender=prerender,
                 remove_calls=remove_calls,
                 auth=auth,
                 resource_callback=ctx.get_resource,
                 retry_count=kwargs.get('retry_count', 1),
                 retry_sleep=kwargs.get('retry_sleep', 15))
    else:
        ctx.logger.debug('No calls.')
예제 #14
0
def _scaledown_group_to_settings(ctx, list_scale_groups, scale_compute):
    scale_settings = {}
    for scalable_entity_name in list_scale_groups:
        delta = list_scale_groups[scalable_entity_name]['count']
        instances_remove = list_scale_groups[scalable_entity_name]['values']
        ctx.logger.info('Scale down {} by delta: {}'
                        .format(repr(scalable_entity_name),
                                repr(delta)))
        if delta == 0:
            ctx.logger.info('delta parameter is 0, so no scaling will '
                            'take place.')
            continue

        scaling_group = ctx.deployment.scaling_groups.get(scalable_entity_name)
        if scaling_group:
            curr_num_instances = (
                scaling_group['properties']['current_instances']
            )
            planned_num_instances = curr_num_instances - delta
            scale_id = scalable_entity_name
        else:
            node = ctx.get_node(scalable_entity_name)
            if not node:
                raise ValueError("No scalable entity named {0} was found"
                                 .format(scalable_entity_name))
            host_node = node.host_node
            scaled_node = host_node if (scale_compute and host_node) else node
            curr_num_instances = scaled_node.number_of_instances
            planned_num_instances = curr_num_instances - delta
            scale_id = scaled_node.id

        scale_settings[scale_id] = {
            'instances': planned_num_instances,
            'removed_ids_include_hint': instances_remove,
        }

    ctx.logger.info(
        'Scale settings: {}'.format(repr(obfuscate_passwords(scale_settings))))
    return scale_settings
 def test_obfuscate_passwords(self):
     call = {
         'host': 'localhost',
         'auth': {
             'user': '******',
             'password': '******'
         },
         'port': -1,
         'response_translation': {
             "object": ["object_id"]
         }
     }
     obfuscated_call = {
         'host': 'localhost',
         'auth': {
             'user': '******',
             'password': '******'
         },
         'port': -1,
         'response_translation': {
             "object": ["object_id"]
         }
     }
     self.assertEqual(filters.obfuscate_passwords(call), obfuscated_call)
예제 #16
0
    def _get_token(self):
        self.logger.debug('Checking Kubernetes authentication options.')

        for variant in self.VARIANTS:
            try:
                candidate = variant(self.logger, self.authentication_data) \
                    .get_token()
                self.logger.debug(
                    'Authentication option {variant} will be used'.format(
                        variant=variant.__name__)
                )
                return candidate
            except HelmKuberentesAuthenticationError:
                self.logger.debug(
                    'Authentication option {variant} cannot be used'.format(
                        variant=variant.__name__)
                )

        self.logger.debug(
            'Cannot generate Bearer token - no suitable authentication '
            'variant found for {props} properties'.format(
                props=obfuscate_passwords(self.authentication_data))
        )
        return None
def scaledownlist(ctx,
                  scale_compute=False,
                  ignore_failure=False,
                  force_db_cleanup=False,
                  scale_transaction_field=u'',
                  scale_node_name=None,
                  scale_node_field=u'',
                  scale_node_field_value=u'',
                  all_results=False,
                  node_sequence=None,
                  **_):
    if not scale_node_field:
        raise ValueError('You should provide `scale_node_field` for correct'
                         'downscale.')

    if isinstance(scale_node_field_value, text_type):
        scale_node_field_value = [scale_node_field_value]

    ctx.logger.debug("Filter by values list: {}.".format(
        repr(obfuscate_passwords(scale_node_field_value))))

    if not scale_node_name:
        scale_node_name = None
        ctx.logger.debug("Will be searched by all instances.")

    if isinstance(scale_node_name, text_type):
        scale_node_name = [scale_node_name]

    if isinstance(scale_node_field, text_type):
        scale_node_field = [scale_node_field]

    instances, instance_ids = _get_transaction_instances(
        ctx=ctx,
        scale_transaction_field=scale_transaction_field,
        scale_node_names=scale_node_name,
        scale_node_field_path=scale_node_field,
        scale_node_field_values=scale_node_field_value,
        all_results=all_results)

    if not instance_ids:
        ctx.logger.info("Empty list for instances for remove.")
        return

    # we have list of instances_id(string) as part of scale dictionary
    scale_settings = _scaledown_group_to_settings(
        ctx, _get_scale_list(ctx, instances, text_type), scale_compute)

    try:
        _run_scale_settings(ctx,
                            scale_settings, {},
                            instances_remove_ids=instance_ids,
                            ignore_failure=ignore_failure,
                            node_sequence=node_sequence)
    except Exception as e:
        ctx.logger.info('Scale down based on transaction failed: {}'.format(
            repr(e)))
        # check list for forced remove
        removed = []
        for node in ctx.nodes:
            for instance in node.instances:
                if instance.id in instance_ids:
                    removed.append(instance)
        _uninstall_instances(ctx=ctx,
                             graph=ctx.graph_mode(),
                             removed=removed,
                             related=[],
                             ignore_failure=ignore_failure,
                             node_sequence=node_sequence)

        # remove from DB
        if force_db_cleanup:
            ctx.logger.warn('Ignoring force_db_cleanup. Deprecated feature.')
예제 #18
0
def process(params, template, request_props, prerender=False,
            resource_callback=False):
    logger.info(
        'Template:\n{}'.format(shorted_text(obfuscate_passwords(template))))
    if prerender:
        rendered_call = render_template(template, params)
        template_yaml = yaml.safe_load(rendered_call)
    else:
        template_yaml = yaml.safe_load(template)
    result_properties = {}
    calls = []
    if not template_yaml or not template_yaml.get('rest_calls'):
        logger.debug('Empty call list')
        return {}

    for call in template_yaml['rest_calls']:
        call_with_request_props = request_props.copy()
        logger.debug(
            'Call: {}'.format(shorted_text(obfuscate_passwords(call))))
        # enrich params with items stored in runtime props by prev calls
        params.update(result_properties)
        if not prerender:
            call = "{0}".format(call)
            # Remove quotation marks before and after jinja blocks
            call = re.sub(r'\'\{\%', '{%', call)
            call = re.sub(r'\%\}\'', '%}', call)
            rendered_call = render_template(call, params)
            call = ast.literal_eval(rendered_call)
        calls.append(call)
        logger.debug('Rendered call: {}'.format(
            shorted_text(obfuscate_passwords(call))))
        call_with_request_props.update(call)

        # client/server side certification check
        file_to_remove = []
        for field in ['verify', 'cert']:
            if isinstance(call_with_request_props.get(field), string_types):
                if not os.path.isfile(call_with_request_props.get(field)):
                    fd, destination = tempfile.mkstemp()
                    os.write(fd, call_with_request_props.get(field).encode())
                    os.close(fd)
                    # replace to path to content
                    call_with_request_props[field] = destination
                    file_to_remove.append(destination)

        # run requests
        try:
            response = _send_request(call_with_request_props,
                                     resource_callback=resource_callback)
        finally:
            for path in file_to_remove:
                try:
                    os.remove(path)
                except Exception as e:
                    logger.debug(
                        'Cant remove temporary file {path}: {error}'
                        .format(path=path, error=repr(e))
                    )
        _process_response(response, call, result_properties)
    result_properties = {'result_properties': result_properties,
                         'calls': calls}
    return result_properties
예제 #19
0
def _execute(ctx, properties, runtime_properties, get_resource, host_ip,
             log_stamp, kwargs):
    # register logger file
    logger_file = kwargs.get('logger_file')
    if logger_file:
        fh = logging.FileHandler(logger_file)
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(
            logging.Formatter(fmt="%(asctime)s %(levelname)s %(message)s",
                              datefmt="%Y-%m-%d %H:%M:%S"))
        ctx.logger.addHandler(fh)

    # get current calls
    calls = kwargs.get('calls', [])
    if not calls:
        ctx.logger.info("No calls")
        return

    # credentials
    terminal_auth = properties.get('terminal_auth', {})
    terminal_auth.update(kwargs.get('terminal_auth', {}))
    ip_list = terminal_auth.get('ip')

    # if node contained in some other node, try to overwrite ip
    if not ip_list and host_ip:
        ip_list = [host_ip]
        ctx.logger.info("Used host from container: {ip_list}".format(
            ip_list=repr(ip_list)))

    if isinstance(ip_list, bytes):
        ip_list = ip_list.decode('UTF-8')
    if isinstance(ip_list, text_type):
        ip_list = [ip_list]
    user = terminal_auth.get('user')
    password = terminal_auth.get('password')
    key_content = terminal_auth.get('key_content')
    port = terminal_auth.get('port', 22)

    if not ip_list or not user:
        raise cfy_exc.NonRecoverableError(
            "please check your credentials, ip or user not set")

    # additional settings
    global_promt_check = terminal_auth.get('promt_check')
    global_warning_examples = terminal_auth.get('warnings', [])
    global_error_examples = terminal_auth.get('errors', [])
    global_critical_examples = terminal_auth.get('criticals', [])
    global_responses = terminal_auth.get('responses', [])
    exit_command = terminal_auth.get('exit_command', 'exit')
    smart_device = terminal_auth.get('smart_device')

    # save logs to debug file
    log_file_name = None
    if terminal_auth.get('store_logs'):
        log_file_name = "/tmp/terminal-{log_stamp}.log".format(
            log_stamp=log_stamp)
        ctx.logger.info("Communication logs will be saved to %s" %
                        log_file_name)

    if smart_device:
        ctx.logger.info("Used ssh shell extension.")
        connection = terminal_connection.SmartConnection(
            logger=ctx.logger, log_file_name=log_file_name)
    else:
        ctx.logger.info("Used raw stream connection.")
        connection = terminal_connection.RawConnection(
            logger=ctx.logger, log_file_name=log_file_name)

    for ip in ip_list:
        try:
            prompt = connection.connect(ip,
                                        user,
                                        password,
                                        key_content,
                                        port,
                                        prompt_check=global_promt_check,
                                        responses=global_responses)
            ctx.logger.info("Will be used: " + ip)
            break

        except Exception as ex:
            ctx.logger.info(
                "Can't connect to:{} with exception:{} and type:{}".format(
                    repr(ip), str(ex), str(type(ex))))
    else:
        raise cfy_exc.OperationRetry(message="Let's try one more time?")

    ctx.logger.info(
        "Device prompt: {prompt}".format(prompt=shorted_text(prompt)))

    for call in calls:
        responses = call.get('responses', global_responses)
        promt_check = call.get('promt_check', global_promt_check)
        error_examples = call.get('errors', global_error_examples)
        warning_examples = call.get('warnings', global_warning_examples)
        critical_examples = call.get('criticals', global_critical_examples)
        # use action if exist
        operation = call.get('action', "")
        # use template if have
        if not operation and 'template' in call:
            template_name = call.get('template')
            template_params = call.get('params')
            template = get_resource(template_name)
            if not template:
                ctx.logger.info("Empty template.")
                continue
            if not template_params:
                template_params = {}
            # save context for reuse in template
            template_params['ctx'] = ctx
            operation = render_template(template.decode('utf-8'),
                                        template_params)

        # incase of template_text
        if not operation and 'template_text' in call:
            template_params = call.get('params')
            template = call.get('template_text')
            if not template:
                ctx.logger.info("Empty template_text.")
                continue
            if not template_params:
                template_params = {}
            # save context for reuse in template
            template_params['ctx'] = ctx
            operation = render_template(template, template_params)

        if not operation:
            continue

        if responses:
            ctx.logger.info("We have predefined responses: {responses}".format(
                responses=shorted_text(responses)))

        ctx.logger.debug("Template: \n{operation}".format(
            operation=shorted_text(obfuscate_passwords(operation))))

        result = ""
        for op_line in operation.split("\n"):
            # skip empty lines
            if not op_line.strip():
                continue

            ctx.logger.info("Executing template...")
            ctx.logger.debug("Execute: {opline}".format(
                opline=shorted_text(obfuscate_passwords(op_line))))

            result_part = rerun(ctx=ctx,
                                func=connection.run,
                                args=[],
                                kwargs={
                                    "command": op_line,
                                    "prompt_check": promt_check,
                                    "error_examples": error_examples,
                                    "warning_examples": warning_examples,
                                    "critical_examples": critical_examples,
                                    "responses": responses
                                },
                                retry_count=call.get('retry_count', 10),
                                retry_sleep=call.get('retry_sleep', 15))

            if result_part.strip():
                ctx.logger.info(shorted_text(result_part))

            result += (result_part + "\n")
        # save results to runtime properties
        save_to = call.get('save_to')
        if save_to:
            ctx.logger.info(
                "For save: {result}".format(result=shorted_text(result)))
            runtime_properties[save_to] = result.strip()

    while not connection.is_closed() and exit_command:
        ctx.logger.info("Execute close")
        result = rerun(ctx=ctx,
                       func=connection.run,
                       args=[],
                       kwargs={
                           "command": exit_command,
                           "prompt_check": promt_check,
                           "warning_examples": global_warning_examples,
                           "error_examples": global_error_examples,
                           "critical_examples": global_error_examples
                       })
        ctx.logger.info(
            "Result of close: {result}".format(result=shorted_text(result)))
        time.sleep(1)

    connection.close()
def _get_scale_list(ctx, scalable_entity_properties, property_type):
    # scalable_entity_properties - dictionary with such structure:
    # {
    #   node_name: [{runtime_properties}]
    # }
    # property_type - kind of values inside list of node names(types).
    scalable_entity_dict = {}
    scaling_groups = ctx.deployment.scaling_groups
    groups = _deployments_get_groups(ctx)

    ctx.logger.debug("Scale entities: {}".format(
        repr(scalable_entity_properties)))

    if not isinstance(scalable_entity_properties, dict):
        raise ValueError(
            "You use wrong value for 'scalable_entity_properties': {}".format(
                repr(scalable_entity_properties)))

    for node_name in scalable_entity_properties:
        # get node counts
        node_amount = len(scalable_entity_properties[node_name])

        if not isinstance(scalable_entity_properties[node_name], list):
            raise ValueError(
                "You use wrong value for 'scalable_entity_properties' item: {}"
                .format(repr(scalable_entity_properties[node_name])))
        for el in scalable_entity_properties[node_name]:
            if not isinstance(el, property_type):
                raise ValueError(
                    "You use wrong value for runtime properties item: {}".
                    format(repr(scalable_entity_properties[node_name])))
        # get parent group
        for scalegroup in groups:
            # check that we really have such scalling group
            if scalegroup not in scaling_groups:
                continue
            # check node in nodes group
            if node_name in groups[scalegroup]['members']:
                # not selected
                if scalegroup not in scalable_entity_dict:
                    scalable_entity_dict[scalegroup] = {
                        'count': 0,
                        'values': []
                    }
                # already have have such group, scale by max value
                if scalable_entity_dict[scalegroup]['count'] < node_amount:
                    scalable_entity_dict[scalegroup]['count'] = node_amount
                # save instance id's for scale down workflow
                # ignored for scale up
                scalable_entity_dict[scalegroup]['values'] += (
                    scalable_entity_properties[node_name])
                break
        else:
            # no such group
            if node_name not in scalable_entity_dict:
                scalable_entity_dict[node_name] = {'count': 0, 'values': []}
            scalable_entity_dict[node_name]['count'] = node_amount
            scalable_entity_dict[node_name]['values'] += (
                scalable_entity_properties[node_name])

    ctx.logger.info("Scale rules: {}".format(
        repr(obfuscate_passwords(scalable_entity_dict))))
    return scalable_entity_dict
def _run_scale_settings(ctx,
                        scale_settings,
                        scalable_entity_properties,
                        scale_transaction_field=None,
                        scale_transaction_value=None,
                        ignore_failure=False,
                        ignore_rollback_failure=True,
                        instances_remove_ids=None,
                        node_sequence=None):
    modification = ctx.deployment.start_modification(scale_settings)
    ctx.refresh_node_instances()
    graph = ctx.graph_mode()
    try:
        ctx.logger.info('Deployment modification started. '
                        '[modification_id={0}]'.format(modification.id))
        if len(set(modification.added.node_instances)):
            ctx.logger.info('Added: {}'.format(
                repr([
                    node_instance._node_instance.id
                    for node_instance in modification.added.node_instances
                    if node_instance.modification == 'added'
                ])))
            added_and_related = set(modification.added.node_instances)
            added = set(i for i in added_and_related
                        if i.modification == 'added')
            related = added_and_related - added
            try:
                for node_instance in added:
                    properties_updates = scalable_entity_properties.get(
                        node_instance._node_instance.node_id, {})
                    # save properties updates
                    properties = {}
                    if properties_updates:
                        # pop one dict for runtime properties
                        properties.update(properties_updates.pop())
                    # save transaction list
                    if scale_transaction_field:
                        # save original set of instances in scale up.
                        if scale_transaction_value:
                            properties.update({
                                scale_transaction_field:
                                scale_transaction_value
                            })
                        else:
                            properties.update(
                                {scale_transaction_field: modification.id})
                    # check properties to update
                    if properties:
                        ctx.logger.debug(
                            "{}: Updating {} runtime properties by {}".format(
                                node_instance._node_instance.node_id,
                                node_instance._node_instance.id,
                                repr(obfuscate_passwords(properties))))
                        _update_runtime_properties(
                            ctx, node_instance._node_instance.id, properties)
                if node_sequence:
                    subgraph_func = lifecycle.install_node_instance_subgraph
                    _process_node_instances(
                        ctx=ctx,
                        graph=graph,
                        node_instances=added,
                        ignore_failure=ignore_failure,
                        node_instance_subgraph_func=subgraph_func,
                        node_sequence=node_sequence)
                else:
                    lifecycle.install_node_instances(graph=graph,
                                                     node_instances=added,
                                                     related_nodes=related)
            except Exception as ex:
                ctx.logger.error(
                    'Scale out failed, scaling back in. {}'.format(repr(ex)))
                _wait_for_sent_tasks(ctx, graph)
                _uninstall_instances(ctx=ctx,
                                     graph=graph,
                                     removed=added,
                                     related=related,
                                     ignore_failure=ignore_rollback_failure,
                                     node_sequence=node_sequence)
                raise ex

        if len(set(modification.removed.node_instances)):
            ctx.logger.info('Removed: {}'.format(
                repr([
                    node_instance._node_instance.id
                    for node_instance in modification.removed.node_instances
                    if node_instance.modification == 'removed'
                ])))
            removed_and_related = set(modification.removed.node_instances)
            removed = set(i for i in removed_and_related
                          if i.modification == 'removed')
            ctx.logger.info('Proposed: {}'.format(repr(instances_remove_ids)))
            if instances_remove_ids:
                for instance in removed:
                    if instance._node_instance.id not in instances_remove_ids:
                        raise Exception(
                            "Instance {} not in proposed list {}.".format(
                                repr(instance._node_instance.id),
                                repr(instances_remove_ids)))
            related = removed_and_related - removed
            _uninstall_instances(ctx=ctx,
                                 graph=graph,
                                 removed=removed,
                                 ignore_failure=ignore_failure,
                                 related=related,
                                 node_sequence=node_sequence)
    except Exception as ex:
        ctx.logger.warn('Rolling back deployment modification. '
                        '[modification_id={0}]: {1}'.format(
                            modification.id, repr(ex)))
        _wait_for_sent_tasks(ctx, graph)
        modification.rollback()
        raise ex
    else:
        modification.finish()
예제 #22
0
def _process_response(response, call, store_props):
    logger.debug('Process Response: {}'.format(shorted_text(response)))
    logger.debug(
        'Call: {}'.format(shorted_text(obfuscate_passwords(call))))
    logger.debug('Store props: {}'.format(shorted_text(store_props)))
    logger.debug('Store headers: {}'.format(shorted_text(response.headers)))
    translation_version = call.get('translation_format', 'auto')

    # process headers
    if response.headers:
        translate_and_save(logger, response.headers,
                           call.get('header_translation', None),
                           store_props, translation_version)
    # process cookies
    if response.cookies:
        translate_and_save(logger, response.cookies.get_dict(),
                           call.get('cookies_translation', None),
                           store_props, translation_version)
    # process body
    response_format = call.get('response_format', 'auto').lower()
    if response_format == 'auto':
        if response.headers.get('Content-Type'):
            response_content_type = response.headers['Content-Type'].lower()
            if (
                response_content_type.startswith("application/json") or
                response_content_type.startswith("text/json")
            ):
                response_format = 'json'
            elif (
                response_content_type.startswith('text/xml') or
                response_content_type.startswith('application/xml')
            ):
                response_format = 'xml'
            logger.debug('Detected type is {}'.format(repr(response_format)))
        # for backward compatibility set json for unknown types
        if response_format == 'auto':
            response_format = 'json'
    logger.debug('Response format is {}'.format(repr(response_format)))
    if response_format == 'json' or response_format == 'xml':
        if response_format == 'json':
            json = response.json()
        else:  # XML
            json = xmltodict.parse(response.text)
            logger.debug('XML transformed to dict: {}'
                         .format(shorted_text(obfuscate_passwords(json))))

        _check_response(json, call.get('nonrecoverable_response'), False)
        _check_response(json, call.get('response_expectation'), True)

        translate_and_save(logger, json,
                           call.get('response_translation', None),
                           store_props, translation_version)
    elif response_format == 'text':
        store_props['text'] = response.text
    elif response_format == 'raw':
        logger.debug('No action for raw response_format')
    else:
        raise WrongTemplateDataException(
            "Response_format {} is not supported. "
            "Only json/xml or raw response_format is supported".format(
                repr(response_format)))
예제 #23
0
def run_workflow(*args, **kwargs):
    # get current context
    ctx = kwargs.get('ctx', CloudifyContext)
    if ctx.type != context.DEPLOYMENT:
        raise cfy_exc.NonRecoverableError(
            "Called with wrong context: {ctx_type}".format(ctx_type=ctx.type))

    # register logger file
    logger_file = kwargs.get('logger_file')
    if logger_file:
        fh = logging.FileHandler(logger_file)
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(
            logging.Formatter(fmt="%(asctime)s %(levelname)s %(message)s",
                              datefmt="%Y-%m-%d %H:%M:%S"))
        ctx.logger.addHandler(fh)

    # check inputs
    if len(args):
        inputs = args[0]
    else:
        inputs = kwargs.get('inputs', {})

    # dump current parameters
    ctx.logger.debug(
        "Workflow run called with {inputs} and args '{args}' and kwargs:"
        " {kwargs}".format(inputs=repr(obfuscate_passwords(inputs)),
                           args=repr(obfuscate_passwords(args)),
                           kwargs=repr(obfuscate_passwords(kwargs))))

    # check deployment id, strange if empty but lets check
    deployment_id = inputs.get('deployment_id')
    if not deployment_id:
        ctx.logger.error("Deployment id is undefined")
        return

    # get workflow name
    workflow_name = kwargs.get('workflow_for_run')
    if not workflow_name:
        ctx.logger.error("Workflow for run is undefined")
        return

    # get workflow params
    workflow_params = kwargs.get('workflow_params', {})

    # get credentials for rest connection to manager, can be used for run
    # workflow with different user/tenant
    client_config = kwargs.get('client_config', {})
    if client_config:
        client = CloudifyClient(**client_config)
    else:
        # get client from current manager
        client = manager.get_rest_client()

    # get deployment information
    deployment = client.deployments.get(deployment_id=deployment_id)
    if not deployment:
        ctx.logger.error("Deployment disappear.")
        return

    # get filter dictionary
    filter_by = kwargs.get('filter_by', [])
    ctx.logger.debug("Filter {filter_by}".format(filter_by=repr(filter_by)))
    if filter_by:
        # get values from deployment information, use _get_
        # for support 5+ managers
        inputs['deployment_inputs'] = deployment.get('inputs', {})
        inputs['deployment_outputs'] = deployment.get('outputs', {})
        inputs['deployment_capabilities'] = deployment.get('capabilities', {})
        if not _check_filter(ctx=ctx, filter_by=filter_by, inputs=inputs):
            ctx.logger.debug("Event skiped by filter.")
            return

    # mark that we going to run to logs
    ctx.logger.info("Going to {workflow_name} on {deployment_id}".format(
        workflow_name=workflow_name, deployment_id=deployment_id))

    # send uninstall event
    client.executions.start(deployment_id=deployment_id,
                            workflow_id=workflow_name,
                            **workflow_params)
def execute_operation(ctx, operation, operation_kwargs, allow_kwargs_override,
                      run_by_dependency_order, type_names, node_ids,
                      node_instance_ids, node_field, node_field_value,
                      **kwargs):
    """ A generic workflow for executing arbitrary operations on nodes """

    if isinstance(node_field_value, text_type):
        node_field_value = [node_field_value]

    ctx.logger.debug("Filter by values list: {}.".format(
        repr(obfuscate_passwords(node_field_value))))

    graph = ctx.graph_mode()
    subgraphs = {}

    if isinstance(node_field, text_type):
        node_field = [node_field]

    # filtering node instances
    filtered_node_instances = _filter_node_instances(
        ctx=ctx,
        node_ids=node_ids,
        node_instance_ids=node_instance_ids,
        type_names=type_names,
        operation=operation,
        node_field_path=node_field,
        node_field_value=node_field_value)

    if run_by_dependency_order:
        # if run by dependency order is set, then create stub subgraphs for the
        # rest of the instances. This is done to support indirect
        # dependencies, i.e. when instance A is dependent on instance B
        # which is dependent on instance C, where A and C are to be executed
        # with the operation on (i.e. they're in filtered_node_instances)
        # yet B isn't.
        # We add stub subgraphs rather than creating dependencies between A
        # and C themselves since even though it may sometimes increase the
        # number of dependency relationships in the execution graph, it also
        # ensures their number is linear to the number of relationships in
        # the deployment (e.g. consider if A and C are one out of N instances
        # of their respective nodes yet there's a single instance of B -
        # using subgraphs we'll have 2N relationships instead of N^2).
        filtered_node_instances_ids = set(inst.id
                                          for inst in filtered_node_instances)
        for instance in ctx.node_instances:
            if instance.id not in filtered_node_instances_ids:
                subgraphs[instance.id] = graph.subgraph(instance.id)

    # preparing the parameters to the execute_operation call
    exec_op_params = {'kwargs': operation_kwargs, 'operation': operation}
    if allow_kwargs_override is not None:
        exec_op_params['allow_kwargs_override'] = allow_kwargs_override

    # registering actual tasks to sequences
    for instance in filtered_node_instances:
        start_event_message = 'Starting operation {0}'.format(operation)
        if operation_kwargs:
            start_event_message += ' (Operation parameters: {0})'.format(
                repr(operation_kwargs))
        subgraph = graph.subgraph(instance.id)
        sequence = subgraph.sequence()
        sequence.add(
            instance.send_event(start_event_message),
            instance.execute_operation(**exec_op_params),
            instance.send_event('Finished operation {0}'.format(operation)))
        subgraphs[instance.id] = subgraph

    # adding tasks dependencies if required
    if run_by_dependency_order:
        for instance in ctx.node_instances:
            for rel in instance.relationships:
                graph.add_dependency(subgraphs[instance.id],
                                     subgraphs[rel.target_id])

    graph.execute()
예제 #25
0
def _send_request(call, resource_callback=None):
    logger.debug(
        'Request props: {}'.format(shorted_text(obfuscate_passwords(call))))
    port = call['port']
    ssl = call['ssl']
    if port == -1:
        port = 443 if ssl else 80
    if not call.get('hosts', None):
        call['hosts'] = [call['host']]
    for i, host in enumerate(call['hosts']):
        full_url = '{}://{}:{}{}'.format('https' if ssl else 'http', host,
                                         port,
                                         call['path'])
        logger.debug('Full url: {}'.format(repr(full_url)))
        # check if payload can be used as json
        payload_format = call.get('payload_format', 'json')
        payload_data = call.get('payload', None)
        # check that we have some raw payload
        payload_raw = call.get('payload_raw', call.get('raw_payload'))
        if resource_callback and payload_raw:
            payload_data = resource_callback(payload_raw)
        # url params
        params = call.get('params', {})
        # files magic
        files_merged = {}
        files = {}
        files_raw = call.get("files_raw", call.get("raw_files", {}))
        # add all raw files
        for name in files_raw:
            files_merged[name] = resource_callback(files_raw[name])
        # add inline files
        files_merged.update(call.get("files", {}))
        logger.debug('Files merged: {files_merged}'
                     .format(files_merged=shorted_text(files_merged)))
        # convert files strcut to correct type
        for name in files_merged:
            if isinstance(files_merged[name], list):
                # convert to correct struct
                files[name] = tuple(files_merged[name])
            elif isinstance(files_merged[name], string_types):
                # send string as file
                files[name] = StringIO(files_merged[name])
            else:
                # let's request decide about format
                files[name] = files_merged[name]
        logger.debug('Files: {files}'
                     .format(files=shorted_text(files)))
        # combine payloads and params
        if payload_format == 'json':
            json_payload = payload_data
            data = None
        elif payload_format == 'urlencoded' and isinstance(payload_data, dict):
            json_payload = None
            params.update(payload_data)
            data = None
        else:
            json_payload = None
            data = payload_data

        # auth
        if 'auth' not in call:
            auth = None
        else:
            auth = (call['auth'].get('user'), call['auth'].get('password'))

        # run request
        try:
            response = requests.request(call['method'], full_url,
                                        auth=auth,
                                        headers=call.get('headers', None),
                                        verify=call.get('verify', True),
                                        cert=call.get('cert', None),
                                        proxies=call.get('proxies', None),
                                        timeout=call.get('timeout', None),
                                        json=json_payload,
                                        params=params,
                                        files=files if files else None,
                                        data=data)
        except requests.exceptions.ConnectionError as e:
            logger.debug('ConnectionError for host: {}'.format(repr(host)))

            if TEMPLATE_PROPERTY_RETRY_ON_CONNECTION_ERROR in call and \
                    call[TEMPLATE_PROPERTY_RETRY_ON_CONNECTION_ERROR]:

                raise RecoverableResponseException(
                    'ConnectionError {0} has occurred, but flag {1} is set. '
                    'Retrying...'
                    .format(
                        repr(e),
                        TEMPLATE_PROPERTY_RETRY_ON_CONNECTION_ERROR
                    )
                )

            if i == len(call['hosts']) - 1:
                logger.error('No host from list available')
                raise

    logger.info('Response content: \n{}...'
                .format(shorted_text(response.content)))
    logger.info('Status code: {}'.format(repr(response.status_code)))

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError as e:
        logger.debug(repr(e))
        if response.status_code in call.get('recoverable_codes', []):
            raise RecoverableStatusCodeCodeException(
                'Response code {} defined as recoverable'.format(
                    response.status_code))
        if response.status_code not in call.get('successful_codes', []):
            # code is not marked as successful
            raise

        # success?
        logger.debug(
            'Response code {} defined as successful.'.format(
                response.status_code))

    return response