Exemple #1
0
def _do_http(opts, profile='default'):
    '''
    Make the http request and return the data
    '''

    ret = {}

    url = __salt__['config.get']('modjk:{0}:url'.format(profile), '')
    user = __salt__['config.get']('modjk:{0}:user'.format(profile), '')
    passwd = __salt__['config.get']('modjk:{0}:pass'.format(profile), '')
    realm = __salt__['config.get']('modjk:{0}:realm'.format(profile), '')
    timeout = __salt__['config.get']('modjk:{0}:timeout'.format(profile), '')

    if not url:
        raise Exception('missing url in profile {0}'.format(profile))

    if user and passwd:
        auth = _auth(url=url, realm=realm, user=user, passwd=passwd)
        _install_opener(auth)

    url += '?{0}'.format(_urlencode(opts))

    for line in _urlopen(url, timeout=timeout).read().splitlines():
        splt = line.split('=', 1)
        if splt[0] in ret:
            ret[splt[0]] += ',{0}'.format(splt[1])
        else:
            ret[splt[0]] = splt[1]

    return ret
def _query(url,
           username=None,
           api_key=None,
           auth=False,
           args=None,
           header_dict=None,
           method='GET',
           ):
    '''
    Statuscake object method function to construct and execute on the API URL.

    :param username:    The Statuscake username.
    :param api_key:     The Statuscake api key.
    :param function:    The Statuscake api function to perform.
    :param method:      The HTTP method, e.g. GET or POST.
    :param data:        The data to be sent for POST method.
    :return:            The json response from the API call or False.
    '''

    if auth:
        test = _check_api_key(api_key)
        if not test['res']:
            return test
        api_key = test['data']

        test = _check_api_username(username)
        if not test['res']:
            return test
        username = test['data']

    if header_dict is None:
        header_dict = {}

    if method in ['POST', 'PUT']:
        header_dict['Content-Type'] = 'application/x-www-form-urlencoded'

    if auth:
        if 'API' not in header_dict:
            header_dict['API'] = api_key
        if 'Username' not in header_dict:
            header_dict['Username'] = username

    if args:
        args = _urlencode(args)

    result = salt.utils.http.query(
        url,
        method,
        data=args,
        decode=True,
        status=True,
        header_dict=header_dict,
        opts=__opts__,
    )

    if method == 'GET':
        return _handle_get_result(result)
    else:
        return _handle_generic_result(result)
Exemple #3
0
def post_message(channel, message, from_name, api_key=None):
    """
    Send a message to a Slack channel.
    :param channel:     The channel name, either will work.
    :param message:     The message to send to the Slack channel.
    :param from_name:   Specify who the message is from.
    :param api_key:     The Slack api key, if not specified in the configuration.
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' slack.post_message channel="Development Room" message="Build is done" from_name="Build Server"

    """
    if not api_key:
        api_key = _get_api_key()

    if not channel:
        log.error("channel is a required option.")

    # channel must start with a hash
    if not channel.startswith("#"):
        channel = "#{0}".format(channel)

    if not from_name:
        log.error("from_name is a required option.")

    if not message:
        log.error("message is a required option.")

    if not from_name:
        log.error("from_name is a required option.")

    parameters = {"channel": channel, "username": from_name, "text": message}

    # Slack wants the body on POST to be urlencoded.
    result = salt.utils.slack.query(
        function="message",
        api_key=api_key,
        method="POST",
        header_dict={"Content-Type": "application/x-www-form-urlencoded"},
        data=_urlencode(parameters),
        opts=__opts__,
    )

    if result["res"]:
        return True
    else:
        return result
Exemple #4
0
def _post_message(user,
                  device,
                  message,
                  title,
                  priority,
                  expire,
                  retry,
                  sound,
                  api_version=1,
                  token=None):
    '''
    Send a message to a Pushover user or group.
    :param user:        The user or group to send to, must be key of user or group not email address.
    :param message:     The message to send to the PushOver user or group.
    :param title:       Specify who the message is from.
    :param priority     The priority of the message, defaults to 0.
    :param api_version: The PushOver API version, if not specified in the configuration.
    :param notify:      Whether to notify the room, default: False.
    :param token:       The PushOver token, if not specified in the configuration.
    :return:            Boolean if message was sent successfully.
    '''

    user_validate = salt.utils.pushover.validate_user(user, device, token)
    if not user_validate['result']:
        return user_validate

    parameters = dict()
    parameters['user'] = user
    parameters['device'] = device
    parameters['token'] = token
    parameters['title'] = title
    parameters['priority'] = priority
    parameters['expire'] = expire
    parameters['retry'] = retry
    parameters['message'] = message

    if sound:
        sound_validate = salt.utils.pushover.validate_sound(sound, token)
        if sound_validate['res']:
            parameters['sound'] = sound

    result = salt.utils.pushover.query(function='message',
                                       method='POST',
                                       header_dict={'Content-Type': 'application/x-www-form-urlencoded'},
                                       data=_urlencode(parameters),
                                       opts=__opts__)

    return result
Exemple #5
0
def validate_user(user,
                  device,
                  token):
    '''
    Send a message to a Pushover user or group.
    :param user:        The user or group name, either will work.
    :param device:      The device for the user.
    :param token:       The PushOver token.
    '''
    res = {
            'message': 'User key is invalid',
            'result': False
           }

    parameters = dict()
    parameters['user'] = user
    parameters['token'] = token
    if device:
        parameters['device'] = device

    response = query(function='validate_user',
                     method='POST',
                     header_dict={'Content-Type': 'application/x-www-form-urlencoded'},
                     data=_urlencode(parameters))

    if response['res']:
        if 'message' in response:
            _message = response.get('message', '')
            if 'status' in _message:
                if _message.get('dict', {}).get('status', None) == 1:
                    res['result'] = True
                    res['message'] = 'User key is valid.'
                else:
                    res['result'] = False
                    res['message'] = ''.join(_message.get('dict', {}).get('errors'))
    return res
Exemple #6
0
def create(vm_):
    """
    Create a single VM from a data dict
    """
    if "driver" not in vm_:
        vm_["driver"] = vm_["provider"]

    private_networking = config.get_cloud_config_value(
        "enable_private_network", vm_, __opts__, search_global=False, default=False,
    )

    startup_script = config.get_cloud_config_value(
        "startup_script_id", vm_, __opts__, search_global=False, default=None,
    )

    if startup_script and str(startup_script) not in avail_scripts():
        log.error(
            "Your Vultr account does not have a startup script with ID %s",
            str(startup_script),
        )
        return False

    if private_networking is not None:
        if not isinstance(private_networking, bool):
            raise SaltCloudConfigError(
                "'private_networking' should be a boolean value."
            )
    if private_networking is True:
        enable_private_network = "yes"
    else:
        enable_private_network = "no"

    __utils__["cloud.fire_event"](
        "event",
        "starting create",
        "salt/cloud/{0}/creating".format(vm_["name"]),
        args=__utils__["cloud.filter_event"](
            "creating", vm_, ["name", "profile", "provider", "driver"]
        ),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    osid = _lookup_vultrid(vm_["image"], "avail_images", "OSID")
    if not osid:
        log.error("Vultr does not have an image with id or name %s", vm_["image"])
        return False

    vpsplanid = _lookup_vultrid(vm_["size"], "avail_sizes", "VPSPLANID")
    if not vpsplanid:
        log.error("Vultr does not have a size with id or name %s", vm_["size"])
        return False

    dcid = _lookup_vultrid(vm_["location"], "avail_locations", "DCID")
    if not dcid:
        log.error("Vultr does not have a location with id or name %s", vm_["location"])
        return False


    ssh_keys = config.get_cloud_config_value(
        'ssh_key_names',
        get_configured_provider(),
        __opts__,
        search_global=False,
    )

    key_filename = config.get_cloud_config_value(
        'ssh_key_file', vm_, __opts__, search_global=False, default=None
    )

    if key_filename is not None and not os.path.isfile(key_filename):
        raise SaltCloudConfigError(
            'The defined key_filename \'{0}\' does not exist'.format(
                key_filename
            )
        )

    log.debug("Using SSH Key file: '%s'", key_filename)
    log.debug("Selected server SSH keys to install: '%s'", ssh_keys)

    kwargs = {
        "label": vm_["name"],
        "OSID": osid,
        "VPSPLANID": vpsplanid,
        "DCID": dcid,
        "hostname": vm_["name"],
        "enable_private_network": enable_private_network,
    }


    if ssh_keys:
        keyids, available_keys = get_keyids_for_keys_names(ssh_keys)
        if not keyids:
            # None of the requested key names were found.
            raise SaltCloudConfigError(
                'The specified ssh key names were not found in the vultr key list. Desired key names: \'{0}\'. Available keys: \'{1}\''.format(
                    ssh_keys, available_keys
                )
            )

        kwargs['SSHKEYID'] = keyids

    if startup_script:
        kwargs["SCRIPTID"] = startup_script

    log.info("Creating Cloud VM %s", vm_["name"])

    __utils__["cloud.fire_event"](
        "event",
        "requesting instance",
        "salt/cloud/{0}/requesting".format(vm_["name"]),
        args={
            "kwargs": __utils__["cloud.filter_event"](
                "requesting", kwargs, list(kwargs)
            ),
        },
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    try:
        data = _query("server/create", method="POST", data=_urlencode(kwargs))
        if int(data.get("status", "200")) >= 300:
            log.error(
                "Error creating %s on Vultr\n\n" "Vultr API returned %s\n",
                vm_["name"],
                data,
            )
            log.error(
                "Status 412 may mean that you are requesting an\n"
                "invalid location, image, or size."
            )

            __utils__["cloud.fire_event"](
                "event",
                "instance request failed",
                "salt/cloud/{0}/requesting/failed".format(vm_["name"]),
                args={"kwargs": kwargs},
                sock_dir=__opts__["sock_dir"],
                transport=__opts__["transport"],
            )
            return False
    except Exception as exc:  # pylint: disable=broad-except
        log.error(
            "Error creating %s on Vultr\n\n"
            "The following exception was thrown when trying to "
            "run the initial deployment:\n%s",
            vm_["name"],
            exc,
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG,
        )
        __utils__["cloud.fire_event"](
            "event",
            "instance request failed",
            "salt/cloud/{0}/requesting/failed".format(vm_["name"]),
            args={"kwargs": kwargs},
            sock_dir=__opts__["sock_dir"],
            transport=__opts__["transport"],
        )
        return False

    def wait_for_hostname():
        """
        Wait for the IP address to become available
        """
        data = show_instance(vm_["name"], call="action")
        main_ip = six.text_type(data.get("main_ip", "0"))
        if main_ip.startswith("0"):
            time.sleep(3)
            return False
        return data["main_ip"]

    def wait_for_default_password():
        '''
        Wait for the password to become available
        '''
        data = show_instance(vm_['name'], call='action')
        # print("Waiting for default password")
        # pprint.pprint(data)
        if six.text_type(data.get("default_password", "")) == "":
            time.sleep(1)
            return False
        return data["default_password"]

    def wait_for_status():
        """
        Wait for the IP address to become available
        """
        data = show_instance(vm_["name"], call="action")
        # print("Waiting for status normal")
        # pprint.pprint(data)
        if six.text_type(data.get("status", "")) != "active":
            time.sleep(1)
            return False
        return data["default_password"]

    # Maximum delay if we don't see the server
    # reach the 'ok' state.
    install_timeout = 60 * 5

    def wait_for_server_state():
        """
        Wait for the IP address to become available
        """
        data = show_instance(vm_["name"], call="action")
        # print("Waiting for server state ok")
        # pprint.pprint(data)

        # So vultr somehow sits in the 'installingbooting' state for many, MANY minutes after
        # the install has actually finished. I think it might be just a timer somewhere.
        if six.text_type(data.get('server_state', '')) == 'ok':
            return data['default_password']

        if six.text_type(data.get('server_state', '')) == 'installingbooting':
            nonlocal install_timeout
            install_timeout -= 1
            log.debug('Install delay: %s seconds remaining', install_timeout)
            if install_timeout == 0:
                return data['default_password']

        time.sleep(1)
        return False

    vm_["ssh_host"] = __utils__["cloud.wait_for_fun"](
        wait_for_hostname,
        timeout=config.get_cloud_config_value(
            "wait_for_fun_timeout", vm_, __opts__, default=15 * 60
        ),
    )
    __utils__['cloud.wait_for_fun'](
        wait_for_status,
        timeout=config.get_cloud_config_value(
            "wait_for_fun_timeout", vm_, __opts__, default=15 * 60
        ),
    )
    vm_['password'] = __utils__['cloud.wait_for_fun'](
        wait_for_default_password,
        timeout=config.get_cloud_config_value(
            "wait_for_fun_timeout", vm_, __opts__, default=15 * 60
        ),
    )
    __utils__["cloud.wait_for_fun"](
        wait_for_server_state,
        timeout=config.get_cloud_config_value(
            "wait_for_fun_timeout", vm_, __opts__, default=15 * 60
        ),
    )

    __opts__["hard_timeout"] = config.get_cloud_config_value(
        "hard_timeout",
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=None,
    )

    log.debug("VM Created. Host: '%s'", vm_['ssh_host'])
    log.debug("Password: '******'", vm_['password'])

    if key_filename:
        vm_['key_filename'] = key_filename
    # Bootstrap
    ret = __utils__["cloud.bootstrap"](vm_, __opts__)

    ret.update(show_instance(vm_["name"], call="action"))

    log.info("Created Cloud VM '%s'", vm_["name"])
    log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data))

    __utils__["cloud.fire_event"](
        "event",
        "created instance",
        "salt/cloud/{0}/created".format(vm_["name"]),
        args=__utils__["cloud.filter_event"](
            "created", vm_, ["name", "profile", "provider", "driver"]
        ),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    return ret
Exemple #7
0
def create(vm_):
    '''
    Create a single VM from a data dict
    '''
    if 'driver' not in vm_:
        vm_['driver'] = vm_['provider']

    private_networking = config.get_cloud_config_value(
        'enable_private_network',
        vm_,
        __opts__,
        search_global=False,
        default=False,
    )

    startup_script = config.get_cloud_config_value(
        'startup_script_id',
        vm_,
        __opts__,
        search_global=False,
        default=None,
    )

    if startup_script and str(startup_script) not in avail_scripts():
        log.error(
            'Your Vultr account does not have a startup script with ID %s',
            str(startup_script))
        return False

    if private_networking is not None:
        if not isinstance(private_networking, bool):
            raise SaltCloudConfigError(
                "'private_networking' should be a boolean value.")
    if private_networking is True:
        enable_private_network = 'yes'
    else:
        enable_private_network = 'no'

    __utils__['cloud.fire_event'](
        'event',
        'starting create',
        'salt/cloud/{0}/creating'.format(vm_['name']),
        args=__utils__['cloud.filter_event'](
            'creating', vm_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'])

    osid = _lookup_vultrid(vm_['image'], 'avail_images', 'OSID')
    if not osid:
        log.error('Vultr does not have an image with id or name %s',
                  vm_['image'])
        return False

    vpsplanid = _lookup_vultrid(vm_['size'], 'avail_sizes', 'VPSPLANID')
    if not vpsplanid:
        log.error('Vultr does not have a size with id or name %s', vm_['size'])
        return False

    dcid = _lookup_vultrid(vm_['location'], 'avail_locations', 'DCID')
    if not dcid:
        log.error('Vultr does not have a location with id or name %s',
                  vm_['location'])
        return False

    kwargs = {
        'label': vm_['name'],
        'OSID': osid,
        'VPSPLANID': vpsplanid,
        'DCID': dcid,
        'hostname': vm_['name'],
        'enable_private_network': enable_private_network,
    }
    if startup_script:
        kwargs['SCRIPTID'] = startup_script

    log.info('Creating Cloud VM %s', vm_['name'])

    __utils__['cloud.fire_event'](
        'event',
        'requesting instance',
        'salt/cloud/{0}/requesting'.format(vm_['name']),
        args={
            'kwargs':
            __utils__['cloud.filter_event']('requesting', kwargs,
                                            list(kwargs)),
        },
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'],
    )

    try:
        data = _query('server/create', method='POST', data=_urlencode(kwargs))
        if int(data.get('status', '200')) >= 300:
            log.error(
                'Error creating %s on Vultr\n\n'
                'Vultr API returned %s\n', vm_['name'], data)
            log.error('Status 412 may mean that you are requesting an\n'
                      'invalid location, image, or size.')

            __utils__['cloud.fire_event'](
                'event',
                'instance request failed',
                'salt/cloud/{0}/requesting/failed'.format(vm_['name']),
                args={
                    'kwargs': kwargs
                },
                sock_dir=__opts__['sock_dir'],
                transport=__opts__['transport'],
            )
            return False
    except Exception as exc:
        log.error(
            'Error creating %s on Vultr\n\n'
            'The following exception was thrown when trying to '
            'run the initial deployment:\n%s',
            vm_['name'],
            exc,
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG)
        __utils__['cloud.fire_event'](
            'event',
            'instance request failed',
            'salt/cloud/{0}/requesting/failed'.format(vm_['name']),
            args={
                'kwargs': kwargs
            },
            sock_dir=__opts__['sock_dir'],
            transport=__opts__['transport'],
        )
        return False

    def wait_for_hostname():
        '''
        Wait for the IP address to become available
        '''
        data = show_instance(vm_['name'], call='action')
        main_ip = six.text_type(data.get('main_ip', '0'))
        if main_ip.startswith('0'):
            time.sleep(3)
            return False
        return data['main_ip']

    def wait_for_default_password():
        '''
        Wait for the IP address to become available
        '''
        data = show_instance(vm_['name'], call='action')
        # print("Waiting for default password")
        # pprint.pprint(data)
        if six.text_type(data.get('default_password', '')) == '':
            time.sleep(1)
            return False
        return data['default_password']

    def wait_for_status():
        '''
        Wait for the IP address to become available
        '''
        data = show_instance(vm_['name'], call='action')
        # print("Waiting for status normal")
        # pprint.pprint(data)
        if six.text_type(data.get('status', '')) != 'active':
            time.sleep(1)
            return False
        return data['default_password']

    def wait_for_server_state():
        '''
        Wait for the IP address to become available
        '''
        data = show_instance(vm_['name'], call='action')
        # print("Waiting for server state ok")
        # pprint.pprint(data)
        if six.text_type(data.get('server_state', '')) != 'ok':
            time.sleep(1)
            return False
        return data['default_password']

    vm_['ssh_host'] = __utils__['cloud.wait_for_fun'](
        wait_for_hostname,
        timeout=config.get_cloud_config_value('wait_for_fun_timeout',
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )
    vm_['password'] = __utils__['cloud.wait_for_fun'](
        wait_for_default_password,
        timeout=config.get_cloud_config_value('wait_for_fun_timeout',
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )
    __utils__['cloud.wait_for_fun'](
        wait_for_status,
        timeout=config.get_cloud_config_value('wait_for_fun_timeout',
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )
    __utils__['cloud.wait_for_fun'](
        wait_for_server_state,
        timeout=config.get_cloud_config_value('wait_for_fun_timeout',
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )

    __opts__['hard_timeout'] = config.get_cloud_config_value(
        'hard_timeout',
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=None,
    )

    # Bootstrap
    ret = __utils__['cloud.bootstrap'](vm_, __opts__)

    ret.update(show_instance(vm_['name'], call='action'))

    log.info('Created Cloud VM \'%s\'', vm_['name'])
    log.debug('\'%s\' VM creation details:\n%s', vm_['name'],
              pprint.pformat(data))

    __utils__['cloud.fire_event'](
        'event',
        'created instance',
        'salt/cloud/{0}/created'.format(vm_['name']),
        args=__utils__['cloud.filter_event'](
            'created', vm_, ['name', 'profile', 'provider', 'driver']),
        sock_dir=__opts__['sock_dir'],
        transport=__opts__['transport'])

    return ret
Exemple #8
0
def call_hook(message,
              attachment=None,
              color='good',
              short=False,
              identifier=None,
              channel=None,
              username=None,
              icon_emoji=None):
    '''
    Send message to Slack incoming webhook.

    :param message:     The topic of message.
    :param attachment:  The message to send to the Slacke WebHook.
    :param color:       The color of border of left side
    :param short:       An optional flag indicating whether the value is short
                        enough to be displayed side-by-side with other values.
    :param identifier:  The identifier of WebHook.
    :param channel:     The channel to use instead of the WebHook default.
    :param username:    Username to use instead of WebHook default.
    :param icon_emoji:  Icon to use instead of WebHook default.
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' slack.call_hook message='Hello, from SaltStack'

    '''
    base_url = 'https://hooks.slack.com/services/'
    if not identifier:
        identifier = _get_hook_id()

    url = _urljoin(base_url, identifier)

    if not message:
        log.error('message is required option')

    if attachment:
        payload = {
            'attachments': [{
                'fallback': message,
                'color': color,
                'pretext': message,
                'fields': [{
                    "value": attachment,
                    "short": short,
                }]
            }]
        }
    else:
        payload = {
            'text': message,
        }

    if channel:
        payload['channel'] = channel

    if username:
        payload['username'] = username

    if icon_emoji:
        payload['icon_emoji'] = icon_emoji

    data = _urlencode({'payload': json.dumps(payload, ensure_ascii=False)})
    result = salt.utils.http.query(url, method='POST', data=data, status=True)

    if result['status'] <= 201:
        return True
    else:
        return {'res': False, 'message': result.get('body', result['status'])}
Exemple #9
0
def _query(function, api_key=None, api_version=None, room_id=None, method="GET", data=None):
    """
    HipChat object method function to construct and execute on the API URL.

    :param api_key:     The HipChat api key.
    :param function:    The HipChat api function to perform.
    :param api_version: The HipChat api version (v1 or v2).
    :param method:      The HTTP method, e.g. GET or POST.
    :param data:        The data to be sent for POST method.
    :return:            The json response from the API call or False.
    """
    headers = {}
    query_params = {}

    if not api_key or not api_version:
        try:
            options = __salt__["config.option"]("hipchat")
            if not api_key:
                api_key = options.get("api_key")
            if not api_version:
                api_version = options.get("api_version")
        except (NameError, KeyError, AttributeError):
            log.error("No HipChat api key or version found.")
            return False

    if room_id:
        room_id = "room/{0}/notification".format(str(room_id))
    else:
        room_id = "room/0/notification"

    hipchat_functions = {
        "v1": {
            "rooms": {"request": "rooms/list", "response": "rooms"},
            "users": {"request": "users/list", "response": "users"},
            "message": {"request": "rooms/message", "response": "status"},
        },
        "v2": {
            "rooms": {"request": "room", "response": "items"},
            "users": {"request": "user", "response": "items"},
            "message": {"request": room_id, "response": None},
        },
    }

    api_url = "https://api.hipchat.com"
    base_url = _urljoin(api_url, api_version + "/")
    path = hipchat_functions.get(api_version).get(function).get("request")
    url = _urljoin(base_url, path, False)

    if api_version == "v1":
        query_params["format"] = "json"
        query_params["auth_token"] = api_key

        if method == "POST":
            headers["Content-Type"] = "application/x-www-form-urlencoded"

        if data:
            if data.get("notify", None):
                data["notify"] = 1
            data = _urlencode(data)
    elif api_version == "v2":
        headers["Authorization"] = "Bearer {0}".format(api_key)
        if data:
            data = json.dumps(data)

        if method == "POST":
            headers["Content-Type"] = "application/json"
    else:
        log.error("Unsupported HipChat API version")
        return False

    result = salt.utils.http.query(
        url, method, params=query_params, data=data, decode=True, status=True, header_dict=headers, opts=__opts__
    )

    if result.get("status", None) == salt.ext.six.moves.http_client.OK:
        response = hipchat_functions.get(api_version).get(function).get("response")
        return result.get("dict", {}).get(response, None)
    elif result.get("status", None) == salt.ext.six.moves.http_client.NO_CONTENT:
        return False
    else:
        log.debug(url)
        log.debug(query_params)
        log.debug(data)
        log.debug(result)
        if result.get("error"):
            log.error(result)
        return False
Exemple #10
0
def post_message(user=None,
                 device=None,
                 message=None,
                 title=None,
                 priority=None,
                 expire=None,
                 retry=None,
                 sound=None,
                 api_version=1,
                 token=None):
    '''
    Send a message to a Pushover user or group.

    :param user:        The user or group to send to, must be key of user or group not email address.
    :param message:     The message to send to the PushOver user or group.
    :param title:       Specify who the message is from.
    :param priority:    The priority of the message, defaults to 0.
    :param expire:      The message should expire after N number of seconds.
    :param retry:       The number of times the message should be retried.
    :param sound:       The sound to associate with the message.
    :param api_version: The PushOver API version, if not specified in the configuration.
    :param token:       The PushOver token, if not specified in the configuration.
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' pushover.post_message user='******' title='Message from Salt' message='Build is done'

        salt '*' pushover.post_message user='******' title='Message from Salt' message='Build is done' priority='2' expire='720' retry='5'

    '''

    if not token:
        token = __salt__['config.get']('pushover.token') or \
                __salt__['config.get']('pushover:token')
        if not token:
            raise SaltInvocationError('Pushover token is unavailable.')

    if not user:
        user = __salt__['config.get']('pushover.user') or \
               __salt__['config.get']('pushover:user')
        if not user:
            raise SaltInvocationError('Pushover user key is unavailable.')

    if not message:
        raise SaltInvocationError('Required parameter "message" is missing.')

    user_validate = salt.utils.pushover.validate_user(user, device, token)
    if not user_validate['result']:
        return user_validate

    if not title:
        title = 'Message from SaltStack'

    parameters = dict()
    parameters['user'] = user
    parameters['device'] = device
    parameters['token'] = token
    parameters['title'] = title
    parameters['priority'] = priority
    parameters['expire'] = expire
    parameters['retry'] = retry
    parameters['message'] = message

    if sound and salt.utils.pushover.validate_sound(sound, token)['res']:
        parameters['sound'] = sound

    result = salt.utils.pushover.query(
        function='message',
        method='POST',
        header_dict={'Content-Type': 'application/x-www-form-urlencoded'},
        data=_urlencode(parameters),
        opts=__opts__)

    if result['res']:
        return True
    else:
        return result
Exemple #11
0
def query(url,
          method='GET',
          params=None,
          data=None,
          data_file=None,
          header_dict=None,
          header_list=None,
          header_file=None,
          username=None,
          password=None,
          auth=None,
          decode=False,
          decode_type='auto',
          status=False,
          headers=False,
          text=False,
          cookies=None,
          cookie_jar=None,
          cookie_format='lwp',
          persist_session=False,
          session_cookie_jar=None,
          data_render=False,
          data_renderer=None,
          header_render=False,
          header_renderer=None,
          template_dict=None,
          test=False,
          test_url=None,
          node='minion',
          port=80,
          opts=None,
          backend=None,
          ca_bundle=None,
          verify_ssl=None,
          cert=None,
          text_out=None,
          headers_out=None,
          decode_out=None,
          stream=False,
          streaming_callback=None,
          header_callback=None,
          handle=False,
          agent=USERAGENT,
          hide_fields=None,
          raise_error=True,
          **kwargs):
    '''
    Query a resource, and decode the return data
    '''
    ret = {}

    if opts is None:
        if node == 'master':
            opts = salt.config.master_config(
                os.path.join(salt.syspaths.CONFIG_DIR, 'master')
            )
        elif node == 'minion':
            opts = salt.config.minion_config(
                os.path.join(salt.syspaths.CONFIG_DIR, 'minion')
            )
        else:
            opts = {}

    if not backend:
        backend = opts.get('backend', 'tornado')

    match = re.match(r'https?://((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)($|/)', url)
    if not match:
        salt.utils.network.refresh_dns()

    if backend == 'requests':
        if HAS_REQUESTS is False:
            ret['error'] = ('http.query has been set to use requests, but the '
                            'requests library does not seem to be installed')
            log.error(ret['error'])
            return ret
        else:
            requests_log = logging.getLogger('requests')
            requests_log.setLevel(logging.WARNING)

    # Some libraries don't support separation of url and GET parameters
    # Don't need a try/except block, since Salt depends on tornado
    url_full = tornado.httputil.url_concat(url, params) if params else url

    if ca_bundle is None:
        ca_bundle = get_ca_bundle(opts)

    if verify_ssl is None:
        verify_ssl = opts.get('verify_ssl', True)

    if cert is None:
        cert = opts.get('cert', None)

    if data_file is not None:
        data = _render(
            data_file, data_render, data_renderer, template_dict, opts
        )

    # Make sure no secret fields show up in logs
    log_url = sanitize_url(url_full, hide_fields)

    log.debug('Requesting URL %s using %s method', log_url, method)
    log.debug("Using backend: %s", backend)

    if method == 'POST' and log.isEnabledFor(logging.TRACE):
        # Make sure no secret fields show up in logs
        if isinstance(data, dict):
            log_data = data.copy()
            if isinstance(hide_fields, list):
                for item in data:
                    for field in hide_fields:
                        if item == field:
                            log_data[item] = 'XXXXXXXXXX'
            log.trace('Request POST Data: %s', pprint.pformat(log_data))
        else:
            log.trace('Request POST Data: %s', pprint.pformat(data))

    if header_file is not None:
        header_tpl = _render(
            header_file, header_render, header_renderer, template_dict, opts
        )
        if isinstance(header_tpl, dict):
            header_dict = header_tpl
        else:
            header_list = header_tpl.splitlines()

    if header_dict is None:
        header_dict = {}

    if header_list is None:
        header_list = []

    if cookie_jar is None:
        cookie_jar = os.path.join(opts.get('cachedir', salt.syspaths.CACHE_DIR), 'cookies.txt')
    if session_cookie_jar is None:
        session_cookie_jar = os.path.join(opts.get('cachedir', salt.syspaths.CACHE_DIR), 'cookies.session.p')

    if persist_session is True and HAS_MSGPACK:
        # TODO: This is hackish; it will overwrite the session cookie jar with
        # all cookies from this one connection, rather than behaving like a
        # proper cookie jar. Unfortunately, since session cookies do not
        # contain expirations, they can't be stored in a proper cookie jar.
        if os.path.isfile(session_cookie_jar):
            with salt.utils.files.fopen(session_cookie_jar, 'rb') as fh_:
                session_cookies = msgpack.load(fh_)
            if isinstance(session_cookies, dict):
                header_dict.update(session_cookies)
        else:
            with salt.utils.files.fopen(session_cookie_jar, 'wb') as fh_:
                msgpack.dump('', fh_)

    for header in header_list:
        comps = header.split(':')
        if len(comps) < 2:
            continue
        header_dict[comps[0].strip()] = comps[1].strip()

    if not auth:
        if username and password:
            auth = (username, password)

    if agent == USERAGENT:
        agent = '{0} http.query()'.format(agent)
    header_dict['User-agent'] = agent

    if backend == 'requests':
        sess = requests.Session()
        sess.auth = auth
        sess.headers.update(header_dict)
        log.trace('Request Headers: %s', sess.headers)
        sess_cookies = sess.cookies
        sess.verify = verify_ssl
    elif backend == 'urllib2':
        sess_cookies = None
    else:
        # Tornado
        sess_cookies = None

    if cookies is not None:
        if cookie_format == 'mozilla':
            sess_cookies = salt.ext.six.moves.http_cookiejar.MozillaCookieJar(cookie_jar)
        else:
            sess_cookies = salt.ext.six.moves.http_cookiejar.LWPCookieJar(cookie_jar)
        if not os.path.isfile(cookie_jar):
            sess_cookies.save()
        sess_cookies.load()

    if test is True:
        if test_url is None:
            return {}
        else:
            url = test_url
            ret['test'] = True

    if backend == 'requests':
        req_kwargs = {}
        if stream is True:
            if requests.__version__[0] == '0':
                # 'stream' was called 'prefetch' before 1.0, with flipped meaning
                req_kwargs['prefetch'] = False
            else:
                req_kwargs['stream'] = True

        # Client-side cert handling
        if cert is not None:
            if isinstance(cert, six.string_types):
                if os.path.exists(cert):
                    req_kwargs['cert'] = cert
            elif isinstance(cert, list):
                if os.path.exists(cert[0]) and os.path.exists(cert[1]):
                    req_kwargs['cert'] = cert
            else:
                log.error('The client-side certificate path that'
                          ' was passed is not valid: %s', cert)

        result = sess.request(
            method, url, params=params, data=data, **req_kwargs
        )
        result.raise_for_status()
        if stream is True:
            # fake a HTTP response header
            header_callback('HTTP/1.0 {0} MESSAGE'.format(result.status_code))
            # fake streaming the content
            streaming_callback(result.content)
            return {
                'handle': result,
            }

        if handle is True:
            return {
                'handle': result,
                'body': result.content,
            }

        log.debug('Final URL location of Response: %s',
                  sanitize_url(result.url, hide_fields))

        result_status_code = result.status_code
        result_headers = result.headers
        result_text = result.content
        result_cookies = result.cookies
        body = result.content
        if not isinstance(body, six.text_type):
            body = body.decode(result.encoding or 'utf-8')
        ret['body'] = body
    elif backend == 'urllib2':
        request = urllib_request.Request(url_full, data)
        handlers = [
            urllib_request.HTTPHandler,
            urllib_request.HTTPCookieProcessor(sess_cookies)
        ]

        if url.startswith('https'):
            hostname = request.get_host()
            handlers[0] = urllib_request.HTTPSHandler(1)
            if not HAS_MATCHHOSTNAME:
                log.warning('match_hostname() not available, SSL hostname checking '
                            'not available. THIS CONNECTION MAY NOT BE SECURE!')
            elif verify_ssl is False:
                log.warning('SSL certificate verification has been explicitly '
                            'disabled. THIS CONNECTION MAY NOT BE SECURE!')
            else:
                if ':' in hostname:
                    hostname, port = hostname.split(':')
                else:
                    port = 443
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.connect((hostname, int(port)))
                sockwrap = ssl.wrap_socket(
                    sock,
                    ca_certs=ca_bundle,
                    cert_reqs=ssl.CERT_REQUIRED
                )
                try:
                    match_hostname(sockwrap.getpeercert(), hostname)
                except CertificateError as exc:
                    ret['error'] = (
                        'The certificate was invalid. '
                        'Error returned was: %s',
                        pprint.pformat(exc)
                        )
                    return ret

                # Client-side cert handling
                if cert is not None:
                    cert_chain = None
                    if isinstance(cert, six.string_types):
                        if os.path.exists(cert):
                            cert_chain = (cert)
                    elif isinstance(cert, list):
                        if os.path.exists(cert[0]) and os.path.exists(cert[1]):
                            cert_chain = cert
                    else:
                        log.error('The client-side certificate path that was '
                                  'passed is not valid: %s', cert)
                        return
                    if hasattr(ssl, 'SSLContext'):
                        # Python >= 2.7.9
                        context = ssl.SSLContext.load_cert_chain(*cert_chain)
                        handlers.append(urllib_request.HTTPSHandler(context=context))  # pylint: disable=E1123
                    else:
                        # Python < 2.7.9
                        cert_kwargs = {
                            'host': request.get_host(),
                            'port': port,
                            'cert_file': cert_chain[0]
                        }
                        if len(cert_chain) > 1:
                            cert_kwargs['key_file'] = cert_chain[1]
                        handlers[0] = salt.ext.six.moves.http_client.HTTPSConnection(**cert_kwargs)

        opener = urllib_request.build_opener(*handlers)
        for header in header_dict:
            request.add_header(header, header_dict[header])
        request.get_method = lambda: method
        try:
            result = opener.open(request)
        except URLError as exc:
            return {'Error': six.text_type(exc)}
        if stream is True or handle is True:
            return {
                'handle': result,
                'body': result.content,
            }

        result_status_code = result.code
        result_headers = dict(result.info())
        result_text = result.read()
        if 'Content-Type' in result_headers:
            res_content_type, res_params = cgi.parse_header(result_headers['Content-Type'])
            if res_content_type.startswith('text/') and \
                    'charset' in res_params and \
                    not isinstance(result_text, six.text_type):
                result_text = result_text.decode(res_params['charset'])
        if six.PY3 and isinstance(result_text, bytes):
            result_text = result_text.decode('utf-8')
        ret['body'] = result_text
    else:
        # Tornado
        req_kwargs = {}

        # Client-side cert handling
        if cert is not None:
            if isinstance(cert, six.string_types):
                if os.path.exists(cert):
                    req_kwargs['client_cert'] = cert
            elif isinstance(cert, list):
                if os.path.exists(cert[0]) and os.path.exists(cert[1]):
                    req_kwargs['client_cert'] = cert[0]
                    req_kwargs['client_key'] = cert[1]
            else:
                log.error('The client-side certificate path that '
                          'was passed is not valid: %s', cert)

        if isinstance(data, dict):
            data = _urlencode(data)

        if verify_ssl:
            req_kwargs['ca_certs'] = ca_bundle

        max_body = opts.get('http_max_body', salt.config.DEFAULT_MINION_OPTS['http_max_body'])
        connect_timeout = opts.get('http_connect_timeout', salt.config.DEFAULT_MINION_OPTS['http_connect_timeout'])
        timeout = opts.get('http_request_timeout', salt.config.DEFAULT_MINION_OPTS['http_request_timeout'])

        client_argspec = None

        proxy_host = opts.get('proxy_host', None)
        if proxy_host:
            # tornado requires a str for proxy_host, cannot be a unicode str in py2
            proxy_host = salt.utils.stringutils.to_str(proxy_host)
        proxy_port = opts.get('proxy_port', None)
        proxy_username = opts.get('proxy_username', None)
        if proxy_username:
            # tornado requires a str, cannot be unicode str in py2
            proxy_username = salt.utils.stringutils.to_str(proxy_username)
        proxy_password = opts.get('proxy_password', None)
        if proxy_password:
            # tornado requires a str, cannot be unicode str in py2
            proxy_password = salt.utils.stringutils.to_str(proxy_password)
        no_proxy = opts.get('no_proxy', [])

        # Since tornado doesnt support no_proxy, we'll always hand it empty proxies or valid ones
        # except we remove the valid ones if a url has a no_proxy hostname in it
        if urlparse(url_full).hostname in no_proxy:
            proxy_host = None
            proxy_port = None

        # We want to use curl_http if we have a proxy defined
        if proxy_host and proxy_port:
            if HAS_CURL_HTTPCLIENT is False:
                ret['error'] = ('proxy_host and proxy_port has been set. This requires pycurl and tornado, '
                                'but the libraries does not seem to be installed')
                log.error(ret['error'])
                return ret

            tornado.httpclient.AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient')
            client_argspec = salt.utils.args.get_function_argspec(
                    tornado.curl_httpclient.CurlAsyncHTTPClient.initialize)
        else:
            client_argspec = salt.utils.args.get_function_argspec(
                    tornado.simple_httpclient.SimpleAsyncHTTPClient.initialize)

        supports_max_body_size = 'max_body_size' in client_argspec.args

        req_kwargs.update({
            'method': method,
            'headers': header_dict,
            'auth_username': username,
            'auth_password': password,
            'body': data,
            'validate_cert': verify_ssl,
            'allow_nonstandard_methods': True,
            'streaming_callback': streaming_callback,
            'header_callback': header_callback,
            'connect_timeout': connect_timeout,
            'request_timeout': timeout,
            'proxy_host': proxy_host,
            'proxy_port': proxy_port,
            'proxy_username': proxy_username,
            'proxy_password': proxy_password,
            'raise_error': raise_error,
            'decompress_response': False,
        })

        # Unicode types will cause a TypeError when Tornado's curl HTTPClient
        # invokes setopt. Therefore, make sure all arguments we pass which
        # contain strings are str types.
        req_kwargs = salt.utils.data.decode(req_kwargs, to_str=True)

        try:
            download_client = HTTPClient(max_body_size=max_body) \
                if supports_max_body_size \
                else HTTPClient()
            result = download_client.fetch(url_full, **req_kwargs)
        except tornado.httpclient.HTTPError as exc:
            ret['status'] = exc.code
            ret['error'] = six.text_type(exc)
            return ret
        except socket.gaierror as exc:
            if status is True:
                ret['status'] = 0
            ret['error'] = six.text_type(exc)
            return ret

        if stream is True or handle is True:
            return {
                'handle': result,
                'body': result.body,
            }

        result_status_code = result.code
        result_headers = result.headers
        result_text = result.body
        if 'Content-Type' in result_headers:
            res_content_type, res_params = cgi.parse_header(result_headers['Content-Type'])
            if res_content_type.startswith('text/') and \
                    'charset' in res_params and \
                    not isinstance(result_text, six.text_type):
                result_text = result_text.decode(res_params['charset'])
        if six.PY3 and isinstance(result_text, bytes):
            result_text = result_text.decode('utf-8')
        ret['body'] = result_text
        if 'Set-Cookie' in result_headers and cookies is not None:
            result_cookies = parse_cookie_header(result_headers['Set-Cookie'])
            for item in result_cookies:
                sess_cookies.set_cookie(item)
        else:
            result_cookies = None

    if isinstance(result_headers, list):
        result_headers_dict = {}
        for header in result_headers:
            comps = header.split(':')
            result_headers_dict[comps[0].strip()] = ':'.join(comps[1:]).strip()
        result_headers = result_headers_dict

    log.debug('Response Status Code: %s', result_status_code)
    log.trace('Response Headers: %s', result_headers)
    log.trace('Response Cookies: %s', sess_cookies)
    # log.trace("Content: %s", result_text)

    coding = result_headers.get('Content-Encoding', "identity")

    # Requests will always decompress the content, and working around that is annoying.
    if backend != 'requests':
        result_text = __decompressContent(coding, result_text)

    try:
        log.trace('Response Text: %s', result_text)
    except UnicodeEncodeError as exc:
        log.trace('Cannot Trace Log Response Text: %s. This may be due to '
                  'incompatibilities between requests and logging.', exc)

    if text_out is not None:
        with salt.utils.files.fopen(text_out, 'w') as tof:
            tof.write(result_text)

    if headers_out is not None and os.path.exists(headers_out):
        with salt.utils.files.fopen(headers_out, 'w') as hof:
            hof.write(result_headers)

    if cookies is not None:
        sess_cookies.save()

    if persist_session is True and HAS_MSGPACK:
        # TODO: See persist_session above
        if 'set-cookie' in result_headers:
            with salt.utils.files.fopen(session_cookie_jar, 'wb') as fh_:
                session_cookies = result_headers.get('set-cookie', None)
                if session_cookies is not None:
                    msgpack.dump({'Cookie': session_cookies}, fh_)
                else:
                    msgpack.dump('', fh_)

    if status is True:
        ret['status'] = result_status_code

    if headers is True:
        ret['headers'] = result_headers

    if decode is True:
        if decode_type == 'auto':
            content_type = result_headers.get(
                'content-type', 'application/json'
            )
            if 'xml' in content_type:
                decode_type = 'xml'
            elif 'json' in content_type:
                decode_type = 'json'
            elif 'yaml' in content_type:
                decode_type = 'yaml'
            else:
                decode_type = 'plain'

        valid_decodes = ('json', 'xml', 'yaml', 'plain')
        if decode_type not in valid_decodes:
            ret['error'] = (
                'Invalid decode_type specified. '
                'Valid decode types are: {0}'.format(
                    pprint.pformat(valid_decodes)
                )
            )
            log.error(ret['error'])
            return ret

        if decode_type == 'json':
            ret['dict'] = salt.utils.json.loads(result_text)
        elif decode_type == 'xml':
            ret['dict'] = []
            items = ET.fromstring(result_text)
            for item in items:
                ret['dict'].append(xml.to_dict(item))
        elif decode_type == 'yaml':
            ret['dict'] = salt.utils.data.decode(salt.utils.yaml.safe_load(result_text))
        else:
            text = True

        if decode_out:
            with salt.utils.files.fopen(decode_out, 'w') as dof:
                dof.write(result_text)

    if text is True:
        ret['text'] = result_text

    return ret
Exemple #12
0
def post_message(user=None,
                 device=None,
                 message=None,
                 title=None,
                 priority=None,
                 expire=None,
                 retry=None,
                 sound=None,
                 api_version=1,
                 token=None):
    '''
    Send a message to a Pushover user or group.

    :param user:        The user or group to send to, must be key of user or group not email address.
    :param message:     The message to send to the PushOver user or group.
    :param title:       Specify who the message is from.
    :param priority:    The priority of the message, defaults to 0.
    :param expire:      The message should expire after N number of seconds.
    :param retry:       The number of times the message should be retried.
    :param sound:       The sound to associate with the message.
    :param api_version: The PushOver API version, if not specified in the configuration.
    :param token:       The PushOver token, if not specified in the configuration.
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' pushover.post_message user='******' title='Message from Salt' message='Build is done'

        salt '*' pushover.post_message user='******' title='Message from Salt' message='Build is done' priority='2' expire='720' retry='5'

    '''

    if not token:
        token = __salt__['config.get']('pushover.token') or \
                __salt__['config.get']('pushover:token')
        if not token:
            raise SaltInvocationError('Pushover token is unavailable.')

    if not user:
        user = __salt__['config.get']('pushover.user') or \
               __salt__['config.get']('pushover:user')
        if not user:
            raise SaltInvocationError('Pushover user key is unavailable.')

    if not message:
        raise SaltInvocationError('Required parameter "message" is missing.')

    user_validate = salt.utils.pushover.validate_user(user, device, token)
    if not user_validate['result']:
        return user_validate

    if not title:
        title = 'Message from SaltStack'

    parameters = dict()
    parameters['user'] = user
    parameters['device'] = device
    parameters['token'] = token
    parameters['title'] = title
    parameters['priority'] = priority
    parameters['expire'] = expire
    parameters['retry'] = retry
    parameters['message'] = message

    if sound and salt.utils.pushover.validate_sound(sound, token)['res']:
        parameters['sound'] = sound

    result = salt.utils.pushover.query(function='message',
                                       method='POST',
                                       header_dict={'Content-Type': 'application/x-www-form-urlencoded'},
                                       data=_urlencode(parameters),
                                       opts=__opts__)

    if result['res']:
        return True
    else:
        return result
Exemple #13
0
def post_message(channel, message, from_name, api_key=None, icon=None):
    """
    Send a message to a Slack channel.

    :param channel:     The channel name, either will work.
    :param message:     The message to send to the Slack channel.
    :param from_name:   Specify who the message is from.
    :param api_key:     The Slack api key, if not specified in the configuration.
    :param icon:        URL to an image to use as the icon for this message
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' slack.post_message channel="Development Room" message="Build is done" from_name="Build Server"

    """
    if not api_key:
        api_key = _get_api_key()

    if not channel:
        log.error("channel is a required option.")

    # channel must start with a hash or an @ (direct-message channels)
    if not channel.startswith("#") and not channel.startswith("@"):
        log.warning(
            "Channel name must start with a hash or @. "
            'Prepending a hash and using "#%s" as '
            "channel name instead of %s",
            channel,
            channel,
        )
        channel = "#{0}".format(channel)

    if not from_name:
        log.error("from_name is a required option.")

    if not message:
        log.error("message is a required option.")

    if not from_name:
        log.error("from_name is a required option.")

    parameters = {"channel": channel, "username": from_name, "text": message}

    if icon is not None:
        parameters["icon_url"] = icon

    # Slack wants the body on POST to be urlencoded.
    result = salt.utils.slack.query(
        function="message",
        api_key=api_key,
        method="POST",
        header_dict={"Content-Type": "application/x-www-form-urlencoded"},
        data=_urlencode(parameters),
        opts=__opts__,
    )

    if result["res"]:
        return True
    else:
        return result
Exemple #14
0
def call_hook(
    message,
    attachment=None,
    color="good",
    short=False,
    identifier=None,
    channel=None,
    username=None,
    icon_emoji=None,
):
    """
    Send message to Slack incoming webhook.

    :param message:     The topic of message.
    :param attachment:  The message to send to the Slacke WebHook.
    :param color:       The color of border of left side
    :param short:       An optional flag indicating whether the value is short
                        enough to be displayed side-by-side with other values.
    :param identifier:  The identifier of WebHook.
    :param channel:     The channel to use instead of the WebHook default.
    :param username:    Username to use instead of WebHook default.
    :param icon_emoji:  Icon to use instead of WebHook default.
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' slack.call_hook message='Hello, from SaltStack'

    """
    base_url = "https://hooks.slack.com/services/"
    if not identifier:
        identifier = _get_hook_id()

    url = _urljoin(base_url, identifier)

    if not message:
        log.error("message is required option")

    if attachment:
        payload = {
            "attachments": [
                {
                    "fallback": message,
                    "color": color,
                    "pretext": message,
                    "fields": [{"value": attachment, "short": short}],
                }
            ]
        }
    else:
        payload = {
            "text": message,
        }

    if channel:
        payload["channel"] = channel

    if username:
        payload["username"] = username

    if icon_emoji:
        payload["icon_emoji"] = icon_emoji

    data = _urlencode({"payload": salt.utils.json.dumps(payload)})
    result = salt.utils.http.query(url, method="POST", data=data, status=True)

    if result["status"] <= 201:
        return True
    else:
        return {"res": False, "message": result.get("body", result["status"])}
Exemple #15
0
def query(url,
          method="GET",
          params=None,
          data=None,
          data_file=None,
          header_dict=None,
          header_list=None,
          header_file=None,
          username=None,
          password=None,
          auth=None,
          decode=False,
          decode_type="auto",
          status=False,
          headers=False,
          text=False,
          cookies=None,
          cookie_jar=None,
          cookie_format="lwp",
          persist_session=False,
          session_cookie_jar=None,
          data_render=False,
          data_renderer=None,
          header_render=False,
          header_renderer=None,
          template_dict=None,
          test=False,
          test_url=None,
          node="minion",
          port=80,
          opts=None,
          backend=None,
          ca_bundle=None,
          verify_ssl=None,
          cert=None,
          text_out=None,
          headers_out=None,
          decode_out=None,
          stream=False,
          streaming_callback=None,
          header_callback=None,
          handle=False,
          agent=USERAGENT,
          hide_fields=None,
          raise_error=True,
          formdata=False,
          formdata_fieldname=None,
          formdata_filename=None,
          decode_body=True,
          **kwargs):
    """
    Query a resource, and decode the return data
    """
    ret = {}

    if opts is None:
        if node == "master":
            opts = salt.config.master_config(
                os.path.join(salt.syspaths.CONFIG_DIR, "master"))
        elif node == "minion":
            opts = salt.config.minion_config(
                os.path.join(salt.syspaths.CONFIG_DIR, "minion"))
        else:
            opts = {}

    if not backend:
        backend = opts.get("backend", "tornado")

    match = re.match(
        r"https?://((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)($|/)",
        url,
    )
    if not match:
        salt.utils.network.refresh_dns()

    if backend == "requests":
        if HAS_REQUESTS is False:
            ret["error"] = ("http.query has been set to use requests, but the "
                            "requests library does not seem to be installed")
            log.error(ret["error"])
            return ret
        else:
            requests_log = logging.getLogger("requests")
            requests_log.setLevel(logging.WARNING)

    # Some libraries don't support separation of url and GET parameters
    # Don't need a try/except block, since Salt depends on tornado
    url_full = salt.ext.tornado.httputil.url_concat(url,
                                                    params) if params else url

    if ca_bundle is None:
        ca_bundle = get_ca_bundle(opts)

    if verify_ssl is None:
        verify_ssl = opts.get("verify_ssl", True)

    if cert is None:
        cert = opts.get("cert", None)

    if data_file is not None:
        data = _render(data_file, data_render, data_renderer, template_dict,
                       opts)

    # Make sure no secret fields show up in logs
    log_url = sanitize_url(url_full, hide_fields)

    log.debug("Requesting URL %s using %s method", log_url, method)
    log.debug("Using backend: %s", backend)

    if method == "POST" and log.isEnabledFor(logging.TRACE):
        # Make sure no secret fields show up in logs
        if isinstance(data, dict):
            log_data = data.copy()
            if isinstance(hide_fields, list):
                for item in data:
                    for field in hide_fields:
                        if item == field:
                            log_data[item] = "XXXXXXXXXX"
            log.trace("Request POST Data: %s", pprint.pformat(log_data))
        else:
            log.trace("Request POST Data: %s", pprint.pformat(data))

    if header_file is not None:
        header_tpl = _render(header_file, header_render, header_renderer,
                             template_dict, opts)
        if isinstance(header_tpl, dict):
            header_dict = header_tpl
        else:
            header_list = header_tpl.splitlines()

    if header_dict is None:
        header_dict = {}

    if header_list is None:
        header_list = []

    if cookie_jar is None:
        cookie_jar = os.path.join(
            opts.get("cachedir", salt.syspaths.CACHE_DIR), "cookies.txt")
    if session_cookie_jar is None:
        session_cookie_jar = os.path.join(
            opts.get("cachedir", salt.syspaths.CACHE_DIR), "cookies.session.p")

    if persist_session is True and salt.utils.msgpack.HAS_MSGPACK:
        # TODO: This is hackish; it will overwrite the session cookie jar with
        # all cookies from this one connection, rather than behaving like a
        # proper cookie jar. Unfortunately, since session cookies do not
        # contain expirations, they can't be stored in a proper cookie jar.
        if os.path.isfile(session_cookie_jar):
            with salt.utils.files.fopen(session_cookie_jar, "rb") as fh_:
                session_cookies = salt.utils.msgpack.load(fh_)
            if isinstance(session_cookies, dict):
                header_dict.update(session_cookies)
        else:
            with salt.utils.files.fopen(session_cookie_jar, "wb") as fh_:
                salt.utils.msgpack.dump("", fh_)

    for header in header_list:
        comps = header.split(":")
        if len(comps) < 2:
            continue
        header_dict[comps[0].strip()] = comps[1].strip()

    if not auth:
        if username and password:
            auth = (username, password)

    if agent == USERAGENT:
        agent = "{0} http.query()".format(agent)
    header_dict["User-agent"] = agent

    if backend == "requests":
        sess = requests.Session()
        sess.auth = auth
        sess.headers.update(header_dict)
        log.trace("Request Headers: %s", sess.headers)
        sess_cookies = sess.cookies
        sess.verify = verify_ssl
    elif backend == "urllib2":
        sess_cookies = None
    else:
        # Tornado
        sess_cookies = None

    if cookies is not None:
        if cookie_format == "mozilla":
            sess_cookies = salt.ext.six.moves.http_cookiejar.MozillaCookieJar(
                cookie_jar)
        else:
            sess_cookies = salt.ext.six.moves.http_cookiejar.LWPCookieJar(
                cookie_jar)
        if not os.path.isfile(cookie_jar):
            sess_cookies.save()
        sess_cookies.load()

    if test is True:
        if test_url is None:
            return {}
        else:
            url = test_url
            ret["test"] = True

    if backend == "requests":
        req_kwargs = {}
        if stream is True:
            if requests.__version__[0] == "0":
                # 'stream' was called 'prefetch' before 1.0, with flipped meaning
                req_kwargs["prefetch"] = False
            else:
                req_kwargs["stream"] = True

        # Client-side cert handling
        if cert is not None:
            if isinstance(cert, six.string_types):
                if os.path.exists(cert):
                    req_kwargs["cert"] = cert
            elif isinstance(cert, list):
                if os.path.exists(cert[0]) and os.path.exists(cert[1]):
                    req_kwargs["cert"] = cert
            else:
                log.error(
                    "The client-side certificate path that"
                    " was passed is not valid: %s",
                    cert,
                )

        if formdata:
            if not formdata_fieldname:
                ret["error"] = "formdata_fieldname is required when formdata=True"
                log.error(ret["error"])
                return ret
            result = sess.request(method,
                                  url,
                                  params=params,
                                  files={
                                      formdata_fieldname:
                                      (formdata_filename, StringIO(data))
                                  },
                                  **req_kwargs)
        else:
            result = sess.request(method,
                                  url,
                                  params=params,
                                  data=data,
                                  **req_kwargs)
        result.raise_for_status()
        if stream is True:
            # fake a HTTP response header
            header_callback("HTTP/1.0 {0} MESSAGE".format(result.status_code))
            # fake streaming the content
            streaming_callback(result.content)
            return {
                "handle": result,
            }

        if handle is True:
            return {
                "handle": result,
                "body": result.content,
            }

        log.debug("Final URL location of Response: %s",
                  sanitize_url(result.url, hide_fields))

        result_status_code = result.status_code
        result_headers = result.headers
        result_text = result.content
        result_cookies = result.cookies
        body = result.content
        if not isinstance(body, six.text_type) and decode_body:
            body = body.decode(result.encoding or "utf-8")
        ret["body"] = body
    elif backend == "urllib2":
        request = urllib_request.Request(url_full, data)
        handlers = [
            urllib_request.HTTPHandler,
            urllib_request.HTTPCookieProcessor(sess_cookies),
        ]

        if url.startswith("https"):
            hostname = request.get_host()
            handlers[0] = urllib_request.HTTPSHandler(1)
            if not HAS_MATCHHOSTNAME:
                log.warning(
                    "match_hostname() not available, SSL hostname checking "
                    "not available. THIS CONNECTION MAY NOT BE SECURE!")
            elif verify_ssl is False:
                log.warning("SSL certificate verification has been explicitly "
                            "disabled. THIS CONNECTION MAY NOT BE SECURE!")
            else:
                if ":" in hostname:
                    hostname, port = hostname.split(":")
                else:
                    port = 443
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.connect((hostname, int(port)))
                sockwrap = ssl.wrap_socket(sock,
                                           ca_certs=ca_bundle,
                                           cert_reqs=ssl.CERT_REQUIRED)
                try:
                    match_hostname(sockwrap.getpeercert(), hostname)
                except CertificateError as exc:
                    ret["error"] = (
                        "The certificate was invalid. Error returned was: %s",
                        pprint.pformat(exc),
                    )
                    return ret

                # Client-side cert handling
                if cert is not None:
                    cert_chain = None
                    if isinstance(cert, six.string_types):
                        if os.path.exists(cert):
                            cert_chain = cert
                    elif isinstance(cert, list):
                        if os.path.exists(cert[0]) and os.path.exists(cert[1]):
                            cert_chain = cert
                    else:
                        log.error(
                            "The client-side certificate path that was "
                            "passed is not valid: %s",
                            cert,
                        )
                        return
                    if hasattr(ssl, "SSLContext"):
                        # Python >= 2.7.9
                        context = ssl.SSLContext.load_cert_chain(*cert_chain)
                        handlers.append(
                            urllib_request.HTTPSHandler(context=context))  # pylint: disable=E1123
                    else:
                        # Python < 2.7.9
                        cert_kwargs = {
                            "host": request.get_host(),
                            "port": port,
                            "cert_file": cert_chain[0],
                        }
                        if len(cert_chain) > 1:
                            cert_kwargs["key_file"] = cert_chain[1]
                        handlers[
                            0] = salt.ext.six.moves.http_client.HTTPSConnection(
                                **cert_kwargs)

        opener = urllib_request.build_opener(*handlers)
        for header in header_dict:
            request.add_header(header, header_dict[header])
        request.get_method = lambda: method
        try:
            result = opener.open(request)
        except URLError as exc:
            return {"Error": six.text_type(exc)}
        if stream is True or handle is True:
            return {
                "handle": result,
                "body": result.content,
            }

        result_status_code = result.code
        result_headers = dict(result.info())
        result_text = result.read()
        if "Content-Type" in result_headers:
            res_content_type, res_params = cgi.parse_header(
                result_headers["Content-Type"])
            if (res_content_type.startswith("text/")
                    and "charset" in res_params
                    and not isinstance(result_text, six.text_type)):
                result_text = result_text.decode(res_params["charset"])
        if six.PY3 and isinstance(result_text, bytes) and decode_body:
            result_text = result_text.decode("utf-8")
        ret["body"] = result_text
    else:
        # Tornado
        req_kwargs = {}

        # Client-side cert handling
        if cert is not None:
            if isinstance(cert, six.string_types):
                if os.path.exists(cert):
                    req_kwargs["client_cert"] = cert
            elif isinstance(cert, list):
                if os.path.exists(cert[0]) and os.path.exists(cert[1]):
                    req_kwargs["client_cert"] = cert[0]
                    req_kwargs["client_key"] = cert[1]
            else:
                log.error(
                    "The client-side certificate path that "
                    "was passed is not valid: %s",
                    cert,
                )

        if isinstance(data, dict):
            data = _urlencode(data)

        if verify_ssl:
            req_kwargs["ca_certs"] = ca_bundle

        max_body = opts.get("http_max_body",
                            salt.config.DEFAULT_MINION_OPTS["http_max_body"])
        connect_timeout = opts.get(
            "http_connect_timeout",
            salt.config.DEFAULT_MINION_OPTS["http_connect_timeout"],
        )
        timeout = opts.get(
            "http_request_timeout",
            salt.config.DEFAULT_MINION_OPTS["http_request_timeout"],
        )

        client_argspec = None

        proxy_host = opts.get("proxy_host", None)
        if proxy_host:
            # tornado requires a str for proxy_host, cannot be a unicode str in py2
            proxy_host = salt.utils.stringutils.to_str(proxy_host)
        proxy_port = opts.get("proxy_port", None)
        proxy_username = opts.get("proxy_username", None)
        if proxy_username:
            # tornado requires a str, cannot be unicode str in py2
            proxy_username = salt.utils.stringutils.to_str(proxy_username)
        proxy_password = opts.get("proxy_password", None)
        if proxy_password:
            # tornado requires a str, cannot be unicode str in py2
            proxy_password = salt.utils.stringutils.to_str(proxy_password)
        no_proxy = opts.get("no_proxy", [])

        # Since tornado doesnt support no_proxy, we'll always hand it empty proxies or valid ones
        # except we remove the valid ones if a url has a no_proxy hostname in it
        if urlparse(url_full).hostname in no_proxy:
            proxy_host = None
            proxy_port = None

        # We want to use curl_http if we have a proxy defined
        if proxy_host and proxy_port:
            if HAS_CURL_HTTPCLIENT is False:
                ret["error"] = (
                    "proxy_host and proxy_port has been set. This requires pycurl and tornado, "
                    "but the libraries does not seem to be installed")
                log.error(ret["error"])
                return ret

            salt.ext.tornado.httpclient.AsyncHTTPClient.configure(
                "tornado.curl_httpclient.CurlAsyncHTTPClient")
            client_argspec = salt.utils.args.get_function_argspec(
                salt.ext.tornado.curl_httpclient.CurlAsyncHTTPClient.initialize
            )
        else:
            client_argspec = salt.utils.args.get_function_argspec(
                salt.ext.tornado.simple_httpclient.SimpleAsyncHTTPClient.
                initialize)

        supports_max_body_size = "max_body_size" in client_argspec.args

        req_kwargs.update({
            "method": method,
            "headers": header_dict,
            "auth_username": username,
            "auth_password": password,
            "body": data,
            "validate_cert": verify_ssl,
            "allow_nonstandard_methods": True,
            "streaming_callback": streaming_callback,
            "header_callback": header_callback,
            "connect_timeout": connect_timeout,
            "request_timeout": timeout,
            "proxy_host": proxy_host,
            "proxy_port": proxy_port,
            "proxy_username": proxy_username,
            "proxy_password": proxy_password,
            "raise_error": raise_error,
            "decompress_response": False,
        })

        # Unicode types will cause a TypeError when Tornado's curl HTTPClient
        # invokes setopt. Therefore, make sure all arguments we pass which
        # contain strings are str types.
        req_kwargs = salt.utils.data.decode(req_kwargs, to_str=True)

        try:
            download_client = (HTTPClient(max_body_size=max_body)
                               if supports_max_body_size else HTTPClient())
            result = download_client.fetch(url_full, **req_kwargs)
        except salt.ext.tornado.httpclient.HTTPError as exc:
            ret["status"] = exc.code
            ret["error"] = six.text_type(exc)
            return ret
        except (socket.herror, socket.error, socket.timeout,
                socket.gaierror) as exc:
            if status is True:
                ret["status"] = 0
            ret["error"] = six.text_type(exc)
            log.debug("Cannot perform 'http.query': {0} - {1}".format(
                url_full, ret["error"]))
            return ret

        if stream is True or handle is True:
            return {
                "handle": result,
                "body": result.body,
            }

        result_status_code = result.code
        result_headers = result.headers
        result_text = result.body
        if "Content-Type" in result_headers:
            res_content_type, res_params = cgi.parse_header(
                result_headers["Content-Type"])
            if (res_content_type.startswith("text/")
                    and "charset" in res_params
                    and not isinstance(result_text, six.text_type)):
                result_text = result_text.decode(res_params["charset"])
        if six.PY3 and isinstance(result_text, bytes) and decode_body:
            result_text = result_text.decode("utf-8")
        ret["body"] = result_text
        if "Set-Cookie" in result_headers and cookies is not None:
            result_cookies = parse_cookie_header(result_headers["Set-Cookie"])
            for item in result_cookies:
                sess_cookies.set_cookie(item)
        else:
            result_cookies = None

    if isinstance(result_headers, list):
        result_headers_dict = {}
        for header in result_headers:
            comps = header.split(":")
            result_headers_dict[comps[0].strip()] = ":".join(comps[1:]).strip()
        result_headers = result_headers_dict

    log.debug("Response Status Code: %s", result_status_code)
    log.trace("Response Headers: %s", result_headers)
    log.trace("Response Cookies: %s", sess_cookies)
    # log.trace("Content: %s", result_text)

    coding = result_headers.get("Content-Encoding", "identity")

    # Requests will always decompress the content, and working around that is annoying.
    if backend != "requests":
        result_text = __decompressContent(coding, result_text)

    try:
        log.trace("Response Text: %s", result_text)
    except UnicodeEncodeError as exc:
        log.trace(
            "Cannot Trace Log Response Text: %s. This may be due to "
            "incompatibilities between requests and logging.",
            exc,
        )

    if text_out is not None:
        with salt.utils.files.fopen(text_out, "w") as tof:
            tof.write(result_text)

    if headers_out is not None and os.path.exists(headers_out):
        with salt.utils.files.fopen(headers_out, "w") as hof:
            hof.write(result_headers)

    if cookies is not None:
        sess_cookies.save()

    if persist_session is True and salt.utils.msgpack.HAS_MSGPACK:
        # TODO: See persist_session above
        if "set-cookie" in result_headers:
            with salt.utils.files.fopen(session_cookie_jar, "wb") as fh_:
                session_cookies = result_headers.get("set-cookie", None)
                if session_cookies is not None:
                    salt.utils.msgpack.dump({"Cookie": session_cookies}, fh_)
                else:
                    salt.utils.msgpack.dump("", fh_)

    if status is True:
        ret["status"] = result_status_code

    if headers is True:
        ret["headers"] = result_headers

    if decode is True:
        if decode_type == "auto":
            content_type = result_headers.get("content-type",
                                              "application/json")
            if "xml" in content_type:
                decode_type = "xml"
            elif "json" in content_type:
                decode_type = "json"
            elif "yaml" in content_type:
                decode_type = "yaml"
            else:
                decode_type = "plain"

        valid_decodes = ("json", "xml", "yaml", "plain")
        if decode_type not in valid_decodes:
            ret["error"] = ("Invalid decode_type specified. "
                            "Valid decode types are: {0}".format(
                                pprint.pformat(valid_decodes)))
            log.error(ret["error"])
            return ret

        if decode_type == "json":
            ret["dict"] = salt.utils.json.loads(result_text)
        elif decode_type == "xml":
            ret["dict"] = []
            items = ET.fromstring(result_text)
            for item in items:
                ret["dict"].append(xml.to_dict(item))
        elif decode_type == "yaml":
            ret["dict"] = salt.utils.data.decode(
                salt.utils.yaml.safe_load(result_text))
        else:
            text = True

        if decode_out:
            with salt.utils.files.fopen(decode_out, "w") as dof:
                dof.write(result_text)

    if text is True:
        ret["text"] = result_text

    return ret
Exemple #16
0
def _wget(cmd, opts=None, url='http://localhost:8080/manager', timeout=180):
    '''
    A private function used to issue the command to tomcat via the manager
    webapp

    cmd
        the command to execute

    url
        The URL of the server manager webapp (example:
        http://localhost:8080/manager)

    opts
        a dict of arguments

    timeout
        timeout for HTTP request

    Return value is a dict in the from of::

        {
            res: [True|False]
            msg: list of lines we got back from the manager
        }
    '''

    ret = {
        'res': True,
        'msg': []
    }

    # prepare authentication
    auth = _auth(url)
    if auth is False:
        ret['res'] = False
        ret['msg'] = 'missing username and password settings (grain/pillar)'
        return ret

    # prepare URL
    if url[-1] != '/':
        url += '/'
    url6 = url
    url += 'text/{0}'.format(cmd)
    url6 += '{0}'.format(cmd)
    if opts:
        url += '?{0}'.format(_urlencode(opts))
        url6 += '?{0}'.format(_urlencode(opts))

    # Make the HTTP request
    _install_opener(auth)

    try:
        # Trying tomcat >= 7 url
        ret['msg'] = _urlopen(url, timeout=timeout).read().splitlines()
    except Exception:
        try:
            # Trying tomcat6 url
            ret['msg'] = _urlopen(url6, timeout=timeout).read().splitlines()
        except Exception:
            ret['msg'] = 'Failed to create HTTP request'

    if not ret['msg'][0].startswith('OK'):
        ret['res'] = False

    return ret
Exemple #17
0
def _query(function,
           api_key=None,
           api_version=None,
           room_id=None,
           api_url=None,
           method='GET',
           data=None):
    '''
    HipChat object method function to construct and execute on the API URL.

    :param api_key:     The HipChat api key.
    :param function:    The HipChat api function to perform.
    :param api_version: The HipChat api version (v1 or v2).
    :param method:      The HTTP method, e.g. GET or POST.
    :param data:        The data to be sent for POST method.
    :return:            The json response from the API call or False.
    '''
    headers = {}
    query_params = {}

    if room_id:
        room_id = 'room/{0}/notification'.format(str(room_id))
    else:
        room_id = 'room/0/notification'

    hipchat_functions = {
        'v1': {
            'rooms': {
                'request': 'rooms/list',
                'response': 'rooms',
            },
            'users': {
                'request': 'users/list',
                'response': 'users',
            },
            'message': {
                'request': 'rooms/message',
                'response': 'status',
            },
        },
        'v2': {
            'rooms': {
                'request': 'room',
                'response': 'items',
            },
            'users': {
                'request': 'user',
                'response': 'items',
            },
            'message': {
                'request': room_id,
                'response': None,
            },
        },
    }

    api_url = 'https://{0}'.format(api_url)
    base_url = _urljoin(api_url, api_version + '/')
    path = hipchat_functions.get(api_version).get(function).get('request')
    url = _urljoin(base_url, path, False)

    if api_version == 'v1':
        query_params['format'] = 'json'
        query_params['auth_token'] = api_key

        if method == 'POST':
            headers['Content-Type'] = 'application/x-www-form-urlencoded'

        if data:
            if data.get('notify'):
                data['notify'] = 1
            else:
                data['notify'] = 0
            data = _urlencode(data)
    elif api_version == 'v2':
        headers['Content-Type'] = 'application/json'
        headers['Authorization'] = 'Bearer {0}'.format(api_key)
        if data:
            data = json.dumps(data)
    else:
        log.error('Unsupported HipChat API version')
        return False

    result = salt.utils.http.query(
        url,
        method,
        params=query_params,
        data=data,
        decode=True,
        status=True,
        header_dict=headers,
        opts=__opts__,
    )

    if result.get('status', None) == salt.ext.six.moves.http_client.OK:
        response = hipchat_functions.get(api_version).get(function).get('response')
        return result.get('dict', {}).get(response, None)
    elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT:
        return False
    else:
        log.debug(url)
        log.debug(query_params)
        log.debug(data)
        log.debug(result)
        if result.get('error'):
            log.error(result)
        return False
Exemple #18
0
def query(action=None, command=None, args=None, method='GET', data=None):
    '''
    Make a web call to a Parallels provider
    '''
    path = config.get_cloud_config_value(
        'url', get_configured_provider(), __opts__, search_global=False
    )
    auth_handler = _HTTPBasicAuthHandler()
    auth_handler.add_password(
        realm='Parallels Instance Manager',
        uri=path,
        user=config.get_cloud_config_value(
            'user', get_configured_provider(), __opts__, search_global=False
        ),
        passwd=config.get_cloud_config_value(
            'password', get_configured_provider(), __opts__,
            search_global=False
        )
    )
    opener = _build_opener(auth_handler)
    _install_opener(opener)

    if action:
        path += action

    if command:
        path += '/{0}'.format(command)

    if not type(args, dict):
        args = {}

    kwargs = {'data': data}
    if isinstance(data, str) and '<?xml' in data:
        kwargs['headers'] = {
            'Content-type': 'application/xml',
        }

    if args:
        params = _urlencode(args)
        req = _Request(url='{0}?{1}'.format(path, params), **kwargs)
    else:
        req = _Request(url=path, **kwargs)

    req.get_method = lambda: method

    log.debug('{0} {1}'.format(method, req.get_full_url()))
    if data:
        log.debug(data)

    try:
        result = _urlopen(req)
        log.debug(
            'PARALLELS Response Status Code: {0}'.format(
                result.getcode()
            )
        )

        if 'content-length' in result.headers:
            content = result.read()
            result.close()
            items = ET.fromstring(content)
            return items

        return {}
    except URLError as exc:
        log.error(
            'PARALLELS Response Status Code: {0} {1}'.format(
                exc.code,
                exc.msg
            )
        )
        root = ET.fromstring(exc.read())
        log.error(root)
        return {'error': root}
Exemple #19
0
def post_message(channel,
                 message,
                 from_name,
                 api_key=None,
                 icon=None):
    '''
    Send a message to a Slack channel.

    :param channel:     The channel name, either will work.
    :param message:     The message to send to the Slack channel.
    :param from_name:   Specify who the message is from.
    :param api_key:     The Slack api key, if not specified in the configuration.
    :param icon:        URL to an image to use as the icon for this message
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' slack.post_message channel="Development Room" message="Build is done" from_name="Build Server"

    '''
    if not api_key:
        api_key = _get_api_key()

    if not channel:
        log.error('channel is a required option.')

    # channel must start with a hash or an @ (direct-message channels)
    if not channel.startswith('#') and not channel.startswith('@'):
        log.warning('Channel name must start with a hash or @. '
                    'Prepending a hash and using "#%s" as '
                    'channel name instead of %s',
                    channel, channel)
        channel = '#{0}'.format(channel)

    if not from_name:
        log.error('from_name is a required option.')

    if not message:
        log.error('message is a required option.')

    if not from_name:
        log.error('from_name is a required option.')

    parameters = {
        'channel': channel,
        'username': from_name,
        'text': message
    }

    if icon is not None:
        parameters['icon_url'] = icon

    # Slack wants the body on POST to be urlencoded.
    result = salt.utils.slack.query(function='message',
                                    api_key=api_key,
                                    method='POST',
                                    header_dict={'Content-Type': 'application/x-www-form-urlencoded'},
                                    data=_urlencode(parameters),
                                    opts=__opts__)

    if result['res']:
        return True
    else:
        return result
Exemple #20
0
def _query(function,
           api_key=None,
           api_version=None,
           room_id=None,
           api_url=None,
           method='GET',
           data=None):
    '''
    HipChat object method function to construct and execute on the API URL.

    :param api_key:     The HipChat api key.
    :param function:    The HipChat api function to perform.
    :param api_version: The HipChat api version (v1 or v2).
    :param method:      The HTTP method, e.g. GET or POST.
    :param data:        The data to be sent for POST method.
    :return:            The json response from the API call or False.
    '''
    headers = {}
    query_params = {}

    if room_id:
        room_id = 'room/{0}/notification'.format(str(room_id))
    else:
        room_id = 'room/0/notification'

    hipchat_functions = {
        'v1': {
            'rooms': {
                'request': 'rooms/list',
                'response': 'rooms',
            },
            'users': {
                'request': 'users/list',
                'response': 'users',
            },
            'message': {
                'request': 'rooms/message',
                'response': 'status',
            },
        },
        'v2': {
            'rooms': {
                'request': 'room',
                'response': 'items',
            },
            'users': {
                'request': 'user',
                'response': 'items',
            },
            'message': {
                'request': room_id,
                'response': None,
            },
        },
    }

    api_url = 'https://{0}'.format(api_url)
    base_url = _urljoin(api_url, api_version + '/')
    path = hipchat_functions.get(api_version).get(function).get('request')
    url = _urljoin(base_url, path, False)

    if api_version == 'v1':
        query_params['format'] = 'json'
        query_params['auth_token'] = api_key

        if method == 'POST':
            headers['Content-Type'] = 'application/x-www-form-urlencoded'

        if data:
            if data.get('notify'):
                data['notify'] = 1
            else:
                data['notify'] = 0
            data = _urlencode(data)
    elif api_version == 'v2':
        headers['Content-Type'] = 'application/json'
        headers['Authorization'] = 'Bearer {0}'.format(api_key)
        if data:
            data = json.dumps(data)
    else:
        log.error('Unsupported HipChat API version')
        return False

    result = salt.utils.http.query(
        url,
        method,
        params=query_params,
        data=data,
        decode=True,
        status=True,
        header_dict=headers,
        opts=__opts__,
    )

    if result.get('status', None) == salt.ext.six.moves.http_client.OK:
        response = hipchat_functions.get(api_version).get(function).get(
            'response')
        return result.get('dict', {}).get(response, None)
    elif result.get('status',
                    None) == salt.ext.six.moves.http_client.NO_CONTENT:
        return False
    else:
        log.debug(url)
        log.debug(query_params)
        log.debug(data)
        log.debug(result)
        if result.get('error'):
            log.error(result)
        return False
Exemple #21
0
def _wget(cmd, opts=None, url="http://localhost:8080/manager", timeout=180):
    """
    A private function used to issue the command to tomcat via the manager
    webapp

    cmd
        the command to execute

    url
        The URL of the server manager webapp (example:
        http://localhost:8080/manager)

    opts
        a dict of arguments

    timeout
        timeout for HTTP request

    Return value is a dict in the from of::

        {
            res: [True|False]
            msg: list of lines we got back from the manager
        }
    """

    ret = {"res": True, "msg": []}

    # prepare authentication
    auth = _auth(url)
    if auth is False:
        ret["res"] = False
        ret["msg"] = "missing username and password settings (grain/pillar)"
        return ret

    # prepare URL
    if url[-1] != "/":
        url += "/"
    url6 = url
    url += "text/{}".format(cmd)
    url6 += "{}".format(cmd)
    if opts:
        url += "?{}".format(_urlencode(opts))
        url6 += "?{}".format(_urlencode(opts))

    # Make the HTTP request
    _install_opener(auth)

    try:
        # Trying tomcat >= 7 url
        ret["msg"] = _urlopen(url, timeout=timeout).read().splitlines()
    except Exception:  # pylint: disable=broad-except
        try:
            # Trying tomcat6 url
            ret["msg"] = _urlopen(url6, timeout=timeout).read().splitlines()
        except Exception:  # pylint: disable=broad-except
            ret["msg"] = "Failed to create HTTP request"

    # Force all byte strings to utf-8 strings, for python >= 3.4
    for key, value in enumerate(ret["msg"]):
        try:
            ret["msg"][key] = salt.utils.stringutils.to_unicode(value, "utf-8")
        except (UnicodeDecodeError, AttributeError):
            pass

    if not ret["msg"][0].startswith("OK"):
        ret["res"] = False

    return ret
Exemple #22
0
def _wget(cmd, opts=None, url='http://localhost:8080/manager', timeout=180):
    '''
    A private function used to issue the command to tomcat via the manager
    webapp

    cmd
        the command to execute
    url
        the URL of the server manager webapp
        example: http://localhost:8080/manager
    opts
        a dict of arguments
    timeout
        timeout for HTTP request

    return value is a dict in the from of::

        {
            res: [True|False]
            msg: list of lines we got back from the manager
        }
    '''

    ret = {
        'res': True,
        'msg': []
    }

    # prepare authentication
    auth = _auth(url)
    if auth is False:
        ret['res'] = False
        ret['msg'] = 'missing username and password settings (grain/pillar)'
        return ret

    # prepare URL
    if url[-1] != '/':
        url += '/'
    url6 = url
    url += 'text/{0}'.format(cmd)
    url6 += '{0}'.format(cmd)
    if opts:
        url += '?{0}'.format(_urlencode(opts))
        url6 += '?{0}'.format(_urlencode(opts))

    # Make the HTTP request
    _install_opener(auth)

    try:
        # Trying tomcat >= 7 url
        ret['msg'] = _urlopen(url, timeout=timeout).read().splitlines()
    except Exception:
        try:
            # Trying tomcat6 url
            ret['msg'] = _urlopen(url6, timeout=timeout).read().splitlines()
        except Exception:
            ret['msg'] = 'Failed to create HTTP request'

    if not ret['msg'][0].startswith('OK'):
        ret['res'] = False

    return ret
Exemple #23
0
def post_message(channel,
                 message,
                 from_name,
                 api_key=None,
                 icon=None):
    '''
    Send a message to a Slack channel.

    :param channel:     The channel name, either will work.
    :param message:     The message to send to the Slack channel.
    :param from_name:   Specify who the message is from.
    :param api_key:     The Slack api key, if not specified in the configuration.
    :param icon:        URL to an image to use as the icon for this message
    :return:            Boolean if message was sent successfully.

    CLI Example:

    .. code-block:: bash

        salt '*' slack.post_message channel="Development Room" message="Build is done" from_name="Build Server"

    '''
    if not api_key:
        api_key = _get_api_key()

    if not channel:
        log.error('channel is a required option.')

    # channel must start with a hash or an @ (direct-message channels)
    if not channel.startswith('#') and not channel.startswith('@'):
        log.warning('Channel name must start with a hash or @. Prepending a hash and using "#{0}" as channel name instead of {1}'.format(channel, channel))
        channel = '#{0}'.format(channel)

    if not from_name:
        log.error('from_name is a required option.')

    if not message:
        log.error('message is a required option.')

    if not from_name:
        log.error('from_name is a required option.')

    parameters = {
        'channel': channel,
        'username': from_name,
        'text': message
    }

    if icon is not None:
        parameters['icon_url'] = icon

    # Slack wants the body on POST to be urlencoded.
    result = salt.utils.slack.query(function='message',
                                    api_key=api_key,
                                    method='POST',
                                    header_dict={'Content-Type': 'application/x-www-form-urlencoded'},
                                    data=_urlencode(parameters),
                                    opts=__opts__)

    if result['res']:
        return True
    else:
        return result
Exemple #24
0
def call_hook(message,
              attachment=None,
              color='good',
              short=False,
              identifier=None,
              channel=None,
              username=None,
              icon_emoji=None):
    '''
    Send message to Slack incomming webhook.

    :param message:     The topic of message.
    :param attachment:  The message to send to the Slacke WebHook.
    :param color:       The color of border of left side
    :param short:       An optional flag indicating whether the value is short
                        enough to be displayed side-by-side with other values.
    :param identifier:  The identifier of WebHook.
    :param channel:     The channel to use instead of the WebHook default.
    :param username:    Username to use instead of WebHook default.
    :param icon_emoji:  Icon to use instead of WebHook default.
    :return:            Boolean if message was sent successfuly.

    CLI Example:

    .. code-block:: bash

        salt '*' slack.post_hook message='Hello, from SaltStack'

    '''
    base_url = 'https://hooks.slack.com/services/'
    if not identifier:
        identifier = _get_hook_id()

    url = _urljoin(base_url, identifier)

    if not message:
        log.error('message is required option')

    if attachment:
        payload = {
            'attachments': [
                {
                    'fallback': message,
                    'color': color,
                    'pretext': message,
                    'fields': [
                        {
                            "value": attachment,
                            "short": short,
                        }
                    ]
                }
            ]
        }
    else:
        payload = {
            'text': message,
        }

    if channel:
        payload['channel'] = channel

    if username:
        payload['username'] = username

    if icon_emoji:
        payload['icon_emoji'] = icon_emoji

    data = _urlencode(
        {
            'payload': json.dumps(payload, ensure_ascii=False)
        }
    )
    result = salt.utils.http.query(url, 'POST', data=data)

    if result['status'] <= 201:
        return True
    else:
        return {
            'res': False,
            'message': result.get('body', result['status'])
        }
Exemple #25
0
def create(vm_):
    """
    Create a single VM from a data dict
    """
    if "driver" not in vm_:
        vm_["driver"] = vm_["provider"]

    private_networking = config.get_cloud_config_value(
        "enable_private_network",
        vm_,
        __opts__,
        search_global=False,
        default=False,
    )

    startup_script = config.get_cloud_config_value(
        "startup_script_id",
        vm_,
        __opts__,
        search_global=False,
        default=None,
    )

    if startup_script and str(startup_script) not in avail_scripts():
        log.error(
            "Your Vultr account does not have a startup script with ID %s",
            str(startup_script),
        )
        return False

    if private_networking is not None:
        if not isinstance(private_networking, bool):
            raise SaltCloudConfigError(
                "'private_networking' should be a boolean value.")
    if private_networking is True:
        enable_private_network = "yes"
    else:
        enable_private_network = "no"

    __utils__["cloud.fire_event"](
        "event",
        "starting create",
        "salt/cloud/{0}/creating".format(vm_["name"]),
        args=__utils__["cloud.filter_event"](
            "creating", vm_, ["name", "profile", "provider", "driver"]),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    osid = _lookup_vultrid(vm_["image"], "avail_images", "OSID")
    if not osid:
        log.error("Vultr does not have an image with id or name %s",
                  vm_["image"])
        return False

    vpsplanid = _lookup_vultrid(vm_["size"], "avail_sizes", "VPSPLANID")
    if not vpsplanid:
        log.error("Vultr does not have a size with id or name %s", vm_["size"])
        return False

    dcid = _lookup_vultrid(vm_["location"], "avail_locations", "DCID")
    if not dcid:
        log.error("Vultr does not have a location with id or name %s",
                  vm_["location"])
        return False

    kwargs = {
        "label": vm_["name"],
        "OSID": osid,
        "VPSPLANID": vpsplanid,
        "DCID": dcid,
        "hostname": vm_["name"],
        "enable_private_network": enable_private_network,
    }
    if startup_script:
        kwargs["SCRIPTID"] = startup_script

    log.info("Creating Cloud VM %s", vm_["name"])

    __utils__["cloud.fire_event"](
        "event",
        "requesting instance",
        "salt/cloud/{0}/requesting".format(vm_["name"]),
        args={
            "kwargs":
            __utils__["cloud.filter_event"]("requesting", kwargs,
                                            list(kwargs)),
        },
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    try:
        data = _query("server/create", method="POST", data=_urlencode(kwargs))
        if int(data.get("status", "200")) >= 300:
            log.error(
                "Error creating %s on Vultr\n\n"
                "Vultr API returned %s\n",
                vm_["name"],
                data,
            )
            log.error("Status 412 may mean that you are requesting an\n"
                      "invalid location, image, or size.")

            __utils__["cloud.fire_event"](
                "event",
                "instance request failed",
                "salt/cloud/{0}/requesting/failed".format(vm_["name"]),
                args={
                    "kwargs": kwargs
                },
                sock_dir=__opts__["sock_dir"],
                transport=__opts__["transport"],
            )
            return False
    except Exception as exc:  # pylint: disable=broad-except
        log.error(
            "Error creating %s on Vultr\n\n"
            "The following exception was thrown when trying to "
            "run the initial deployment:\n%s",
            vm_["name"],
            exc,
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG,
        )
        __utils__["cloud.fire_event"](
            "event",
            "instance request failed",
            "salt/cloud/{0}/requesting/failed".format(vm_["name"]),
            args={
                "kwargs": kwargs
            },
            sock_dir=__opts__["sock_dir"],
            transport=__opts__["transport"],
        )
        return False

    def wait_for_hostname():
        """
        Wait for the IP address to become available
        """
        data = show_instance(vm_["name"], call="action")
        main_ip = six.text_type(data.get("main_ip", "0"))
        if main_ip.startswith("0"):
            time.sleep(3)
            return False
        return data["main_ip"]

    def wait_for_default_password():
        """
        Wait for the IP address to become available
        """
        data = show_instance(vm_["name"], call="action")
        # print("Waiting for default password")
        # pprint.pprint(data)
        default_password = six.text_type(data.get("default_password", ""))
        if default_password == "" or default_password == "not supported":
            time.sleep(1)
            return False
        return data["default_password"]

    def wait_for_status():
        """
        Wait for the IP address to become available
        """
        data = show_instance(vm_["name"], call="action")
        # print("Waiting for status normal")
        # pprint.pprint(data)
        if six.text_type(data.get("status", "")) != "active":
            time.sleep(1)
            return False
        return data["default_password"]

    def wait_for_server_state():
        """
        Wait for the IP address to become available
        """
        data = show_instance(vm_["name"], call="action")
        # print("Waiting for server state ok")
        # pprint.pprint(data)
        if six.text_type(data.get("server_state", "")) != "ok":
            time.sleep(1)
            return False
        return data["default_password"]

    vm_["ssh_host"] = __utils__["cloud.wait_for_fun"](
        wait_for_hostname,
        timeout=config.get_cloud_config_value("wait_for_fun_timeout",
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )
    vm_["password"] = __utils__["cloud.wait_for_fun"](
        wait_for_default_password,
        timeout=config.get_cloud_config_value("wait_for_fun_timeout",
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )
    __utils__["cloud.wait_for_fun"](
        wait_for_status,
        timeout=config.get_cloud_config_value("wait_for_fun_timeout",
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )
    __utils__["cloud.wait_for_fun"](
        wait_for_server_state,
        timeout=config.get_cloud_config_value("wait_for_fun_timeout",
                                              vm_,
                                              __opts__,
                                              default=15 * 60),
    )

    __opts__["hard_timeout"] = config.get_cloud_config_value(
        "hard_timeout",
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=None,
    )

    # Bootstrap
    ret = __utils__["cloud.bootstrap"](vm_, __opts__)

    ret.update(show_instance(vm_["name"], call="action"))

    log.info("Created Cloud VM '%s'", vm_["name"])
    log.debug("'%s' VM creation details:\n%s", vm_["name"],
              pprint.pformat(data))

    __utils__["cloud.fire_event"](
        "event",
        "created instance",
        "salt/cloud/{0}/created".format(vm_["name"]),
        args=__utils__["cloud.filter_event"](
            "created", vm_, ["name", "profile", "provider", "driver"]),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    return ret