def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            group_name=dict(type='str', required=True),
            group_enabled=dict(type='bool', default=True),
            group_description=dict(type='str', default=''),
            group_members=dict(type='list', default=[]),
            group_state=dict(type='str', choices=['present', 'absent'], default='present'),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of group
    group_name = module.params['group_name']
    group_enabled = str(int(module.params['group_enabled']))
    group_members = module.params['group_members']
    group_state = module.params['group_state']
    group_description = module.params['group_description']
    # Instantiate API connection
    api_url = module.params['api_url']
    api_auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, api_auth, api_ssl_verify)

    # Fetch list of groups
    groups = apiconnection.listObjects('group')

    # Get an empty group object to lookup UUIDs for group members
    empty_group = apiconnection.getObjectByUuid('group', '')
    group_members_uuids = []
    for group_member in group_members:
        for key,value in empty_group['members']:
            if value['value'] == group_member:
                group_members_uuids.append(key)

    # Build dict with desired state
    desired_properties = {
        'enabled': group_enabled,
        'description': group_description,
        'members': ','.join(group_members_uuids)
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if group object with specified name exists
    for group in groups:
        if group['name'] == group_name:
            uuid = group['uuid']
            break
    group_exists = (uuid != '')
    if group_state == 'present':
        if group_exists:
            group = apiconnection.getObjectByName('group', group_name)
            for prop in desired_properties.keys():
                # Special case for members where two lists have to be compared
                if prop == 'members':
                    current_members_keys = apiconnection.getSelectedList(group[prop])
                    if not apiconnection.compareLists(current_members_keys, group_members):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing members: %s => %s' %(current_members_keys, desired_properties[prop]))
                else:
                    if group[prop] != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, group[prop], desired_properties[prop]))
            if not needs_change:
                result = {'changed': False, 'msg': ['Group already present: %s' %group_name]}
            else:
                if not module.check_mode:
                    additional_msg.append(apiconnection.updateObject('group', group_name, changed_properties))
                    if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
                result = {'changed': True, 'msg': ['Group %s must be changed.' %group_name, additional_msg]}
        else:
            if not module.check_mode:
                additional_msg.append(apiconnection.createObject('group', group_name, desired_properties))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['Group %s must be created.' %group_name, additional_msg]}
    else:
        if group_exists:
            if not module.check_mode:
                additional_msg.append(apiconnection.deleteObject('group', group_name))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['Group %s must be deleted.' %group_name, additional_msg]}
        else:
            result = {'changed': False, 'msg': ['Group %s is not present.' %group_name]}

    module.exit_json(**result)
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            user_name=dict(type='str', required=True),
            user_password=dict(type='str', default='', no_log=True),
            user_enabled=dict(type='bool', default=True),
            user_description=dict(type='str', default=''),
            user_state=dict(type='str', choices=['present', 'absent'], default='present'),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of user
    user_name = module.params['user_name']
    user_password = module.params['user_password']
    user_enabled = str(int(module.params['user_enabled']))
    user_state = module.params['user_state']
    user_description = module.params['user_description']
    # Instantiate API connection
    api_url = module.params['api_url']
    auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, auth, api_ssl_verify)

    # Fetch list of users
    users = apiconnection.listObjects('user')

    # Build dict with desired state
    desired_properties = {'password': user_password, 'enabled': user_enabled, 'description': user_description}
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if user object with specified name exists
    for user in users:
        if user['name'] == user_name:
            user_exists = True
            uuid = user['uuid']
            additional_msg.append(uuid)
            break
    user_exists = (uuid != '')

    if user_state == 'present':
        if user_exists:
            user = apiconnection.getObjectByName('user', user_name)
            for prop in desired_properties.keys():
                if user[prop] != desired_properties[prop]:
                    needs_change = True
                    changed_properties[prop] = desired_properties[prop]
                    additional_msg.append('Changing %s: %s => %s' %(prop, user[prop], desired_properties[prop]))
            if not needs_change:
                result = {'changed': False, 'msg': ['User already present: %s' %user_name]}
            else:
                if not module.check_mode:
                    additional_msg.append(apiconnection.updateObject('user', user_name, changed_properties))
                    if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
                result = {'changed': True, 'msg': ['User %s must be changed.' %user_name, additional_msg]}
        else:
            if not module.check_mode:
                additional_msg.append(apiconnection.createObject('user', user_name, desired_properties))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['User %s must be created.' %user_name, additional_msg]}
    else:
        if user_exists:
            if not module.check_mode:
                additional_msg.append(apiconnection.deleteObject('user', user_name))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['User %s must be deleted.' %user_name, additional_msg]}
        else:
            result = {'changed': False, 'msg': ['User %s is not present.' %user_name]}

    module.exit_json(**result)
Esempio n. 3
0
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            action_name=dict(type='str', required=True),
            action_description=dict(type='str', default=''),
            action_test_type=dict(type='str',
                                  choices=['if', 'unless'],
                                  default='if'),
            action_linked_acls=dict(type='list', default=[]),
            action_operator=dict(type='str',
                                 choices=['and', 'or'],
                                 default='and'),
            action_type=dict(
                type='str',
                choices=[
                    'use_backend', 'use_server', 'map_use_backend',
                    'http-request_allow', 'http-request_deny',
                    'http-request_tarpit', 'http-request_auth',
                    'http-request_redirect', 'http-request_lua',
                    'http-request_use-service', 'http-request_add-header',
                    'http-request_set-header', 'http-request_del-header',
                    'http-request_replace-header',
                    'http-request_replace-value', 'http-request_set-path',
                    'http-response_allow', 'http-response_deny',
                    'http-response_lua', 'http-response_add-header',
                    'http-response_set-header', 'http-response_del-header',
                    'http-response_replace-header',
                    'http-response_replace-value', 'http-response_set-status',
                    'tcp-request_connection_accept',
                    'tcp-request_connection_reject',
                    'tcp-request_content_accept', 'tcp-request_content_reject',
                    'tcp-request_content_lua',
                    'tcp-request_content_use-service',
                    'tcp-request_inspect-delay', 'tcp-response_content_accept',
                    'tcp-response_content_reject', 'tcp-response_content_lua',
                    'tcp-response_inspect-delay', 'custom'
                ],
                required=True),
            action_value=dict(type='str', required=True),
            action_state=dict(type='str',
                              choices=['present', 'absent'],
                              default='present'),
            haproxy_reload=dict(type='bool', default=False)),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of action
    action_name = module.params['action_name']
    action_test_type = module.params['action_test_type']
    action_type = module.params['action_type']
    action_value = module.params['action_value']
    action_linked_acls = module.params['action_linked_acls']
    action_operator = module.params['action_operator']
    action_description = module.params['action_description']
    action_state = module.params['action_state']
    # Instantiate API connection
    api_url = module.params['api_url']
    auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, auth, api_ssl_verify)

    # Fetch list of acls
    actions = apiconnection.listObjects('action')

    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []

    # Replace all dashes in action_type since their keys only use underscores:
    action_type_key = action_type.replace('-', '_')
    # Retrieve an empty action object to lookup UUIDs of linked ACLs
    empty_action = apiconnection.getObjectByUuid('action', '')
    action_linked_acls_uuids = []
    for action_linked_acl in action_linked_acls:
        for key, value in empty_action['linkedAcls'].iteritems():
            if value['value'] == action_linked_acl:
                action_linked_acls_uuids.append(key)
    # Build dict with desired state
    desired_properties = {
        'description': action_description,
        'testType': action_test_type,
        'operator': action_operator,
        'type': action_type,
        action_type_key: action_value,
        'linkedAcls': ','.join(action_linked_acls_uuids)
        #'linkedAcls': ','.join(apiconnection.getUuidsFromNames('acl', action_linked_acls))
    }
    # Special case for several http actions which use 2 fields:
    if 'http' in action_type and 'header' in action_type:
        # del only needs the name of HTTP header to delete
        if 'del' in action_type:
            desired_properties[action_type_key + '_name'] = action_value
        else:
            value_name = action_value.split('::')[0]
            desired_properties[action_type_key + '_name'] = value_name
            # regex actions have _name and _regex
            if 'regex' in action_type:
                value_regex = action_value.split('::')[1]
                desired_properties[action_type_key + '_regex'] = value_regex
            else:
                value_content = action_value.split('::')[1]
                desired_properties[action_type_key +
                                   '_content'] = value_content
    # Special case for http_*_replace_value which also needs http_*_replace_regex
    elif 'http' in action_type and 'value' in action_type:
        value_name = action_value.split('::')[0]
        desired_properties[action_type_key + '_name'] = value_name
        value_regex = action_value.split('::')[1]
        desired_properties[action_type_key + '_regex'] = value_regex
    elif 'http' in action_type and 'status' in action_type:
        value_code = action_value.split('::')[0]
        desired_properties[action_type_key + '_code'] = value_code
        value_reason = action_value.split('::')[1]
        desired_properties[action_type_key + '_reason'] = value_reason
    # Special case for use_backend since it needs a uuid
    elif action_type == 'use_backend':
        #desired_properties[action_type_key] = apiconnection.getUuidByName('backend', action_value)
        backend_uuid = ''
        for key, value in empty_action['use_backend'].iteritems():
            if value['value'] == action_value:
                backend_uuid = key
        desired_properties[action_type_key] = backend_uuid
    else:
        desired_properties[action_type_key] = action_value

    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if action object with specified name exists
    for action in actions:
        if action['name'] == action_name:
            uuid = action['uuid']
            additional_msg.append('Found action with uuid %s' % uuid)
            break
    action_exists = (uuid != '')

    if action_state == 'present':
        if action_exists:
            action = apiconnection.getObjectByName('action', action_name)
            # Check if simple properties differ
            for prop in ['description']:
                if action[prop] != desired_properties[prop]:
                    needs_change = True
                    additional_msg.append('simple prop')
                    changed_properties[prop] = desired_properties[prop]
                    additional_msg.append(
                        'Changing %s: %s => %s' %
                        (prop, action[prop], desired_properties[prop]))
            # Check if action_type_key is already present, if not, replace the whole dict
            if action_type_key not in action:
                # Special cases will be handled a bit further below
                if 'http' in action_type and ('header' in action_type
                                              or 'value' in action_type
                                              or 'status' in action_type):
                    pass
                else:
                    needs_change = True
                    changed_properties = desired_properties
                    additional_msg.append('action_type_key %s not in action' %
                                          action_type_key)
            # Entries in testType, operator and type dicts must be checked separately if property selected == 1:
            complex_types_list = ['testType', 'operator', 'type']
            for complex_type in complex_types_list:
                for key, value in action[complex_type].iteritems():
                    if value['selected'] == '1' and key != desired_properties[
                            complex_type]:
                        # Currently selected type value does not match desired type value, replace the whole dict:
                        additional_msg.append('%s not selected' %
                                              (complex_type))
                        needs_change = True
                        changed_properties = desired_properties
            # Special case where use_backend contains a dict with one selected element
            if not needs_change and action_type == 'use_backend':
                current_use_backend = apiconnection.getSelected(
                    action[action_type_key])
                if current_use_backend != desired_properties[action_type_key]:
                    needs_change = True
                    changed_properties[action_type_key] = desired_properties[
                        action_type_key]
                    additional_msg.append(
                        'Changing %s: %s => %s' %
                        (action_type_key, current_use_backend,
                         desired_properties[action_type_key]))
            # Check special cases where differently named properties exist (HTTP header):
            elif not needs_change and 'http' in action_type and (
                    'header' in action_type or 'value' in action_type
                    or 'status' in action_type):
                if action[action_type_key +
                          '_name'] != desired_properties[action_type_key +
                                                         '_name']:
                    needs_change = True
                    changed_properties[action_type_key +
                                       '_name'] = desired_properties[
                                           action_type_key + '_name']
                for special_property in [
                        action_type_key + '_name', action_type_key + '_regex',
                        action_type_key + '_content'
                ]:
                    if special_property in desired_properties:
                        if special_property not in action:
                            # Current property is missing completely
                            additional_msg.append('property %s missing' %
                                                  special_property)
                            needs_change = True
                            changed_properties = desired_properties
                        else:
                            if action[special_property] != desired_properties[
                                    special_property]:
                                # Current property exists, but value is different, change it
                                additional_msg.append('action value differs')
                                needs_change = True
                                changed_properties[
                                    special_property] = desired_properties[
                                        special_property]

            # Check if value of action needs to be changed:
            elif not needs_change and action_type_key in action:
                if action[action_type_key] != desired_properties[
                        action_type_key]:
                    needs_change = True
                    changed_properties[action_type_key] = desired_properties[
                        action_type_key]
                    additional_msg.append(
                        'Changing %s: %s => %s' %
                        (action_type_key, action[action_type_key],
                         desired_properties[action_type_key]))

            # Check if currently an acl is linked which should not be linked:
            for key, value in action['linkedAcls'].iteritems():
                if value['selected'] == 1:
                    # Get name of linkedAcl and compare with action_linked_acls list
                    #acl = apiconnection.getObjectByUuid('acl', key)
                    acl = empty_action['linkedAcls'][key]['value']
                    additional_msg.append('Found a linked acl with name: %s' %
                                          acl)
                    if acl not in action_linked_acls:
                        # Current element is not in action_linked_acls, rebuild linkedAcls completely
                        needs_change = True
                        additional_msg.append(
                            'linkedAcls containing unwanted element')
                        changed_properties['linkedAcls'] = desired_properties[
                            'linkedAcls']
            # Reverse acl check, make sure every entry in linked_acl is selected in current action object
            for acl in action_linked_acls:
                #acl_uuid = apiconnection.getUuidByName('acl', acl)
                acl_uuid = ''
                for key, value in empty_action['linkedAcls'].iteritems():
                    if value['value'] == acl:
                        acl_uuid = key
                if action['linkedAcls'][acl_uuid]['selected'] == 0:
                    needs_change = True
                    changed_properties['linkedAcls'] = desired_properties[
                        'linkedAcls']
            if not needs_change:
                result = {
                    'changed':
                    False,
                    'msg': [
                        'Action already present: %s' % action_name,
                        additional_msg
                    ]
                }
            else:
                if not module.check_mode:
                    additional_msg.append(
                        apiconnection.updateObject('action', action_name,
                                                   changed_properties))
                    if haproxy_reload:
                        additional_msg.append(apiconnection.applyConfig())
                result = {
                    'changed':
                    True,
                    'msg': [
                        'Action %s must be changed.' % action_name,
                        additional_msg
                    ]
                }
        else:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.createObject('action', action_name,
                                               desired_properties))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed': True,
                'msg':
                ['Action %s must be created.' % action_name, additional_msg]
            }
    else:
        if action_exists:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.deleteObject('action', action_name))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed': True,
                'msg':
                ['Action %s must be deleted.' % action_name, additional_msg]
            }
        else:
            result = {
                'changed': False,
                'msg': ['Action %s is not present.' % action_name]
            }

    module.exit_json(**result)
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            cpu_state=dict(type='str', choices=['present', 'absent'], default='present'),
            cpu_enabled=dict(type='bool', default=True),
            cpu_name=dict(type='str', required=True),
            cpu_process_id=dict(type='str',
                choices=[
                    'all', 'odd', 'even',
                    'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10',
                    'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20',
                    'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30',
                    'x31', 'x32', 'x33', 'x34', 'x35', 'x36', 'x37', 'x38', 'x39', 'x40',
                    'x41', 'x42', 'x43', 'x44', 'x45', 'x46', 'x47', 'x48', 'x49', 'x50',
                    'x51', 'x52', 'x53', 'x54', 'x55', 'x56', 'x57', 'x58', 'x59', 'x60',
                    'x61', 'x62', 'x63',
                ],
                default='all'),
            cpu_thread_id=dict(type='str',
                choices=[
                    'all', 'odd', 'even',
                    'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10',
                    'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20',
                    'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30',
                    'x31', 'x32', 'x33', 'x34', 'x35', 'x36', 'x37', 'x38', 'x39', 'x40',
                    'x41', 'x42', 'x43', 'x44', 'x45', 'x46', 'x47', 'x48', 'x49', 'x50',
                    'x51', 'x52', 'x53', 'x54', 'x55', 'x56', 'x57', 'x58', 'x59', 'x60',
                    'x61', 'x62', 'x63',
                ],
                default='all'),
            cpu_cpu_id=dict(type='list',
                elements='str',
                options=dict(
                    choices=[
                        'all', 'odd', 'even',
                        'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10',
                        'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20',
                        'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30',
                        'x31', 'x32', 'x33', 'x34', 'x35', 'x36', 'x37', 'x38', 'x39', 'x40',
                        'x41', 'x42', 'x43', 'x44', 'x45', 'x46', 'x47', 'x48', 'x49', 'x50',
                        'x51', 'x52', 'x53', 'x54', 'x55', 'x56', 'x57', 'x58', 'x59', 'x60',
                        'x61', 'x62', 'x63',
                    ]
                ),
                default=['all']),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of cpu
    cpu_state = module.params['cpu_state']
    cpu_enabled = str(int(module.params['cpu_enabled']))
    cpu_name = module.params['cpu_name']
    cpu_process_id = module.params['cpu_process_id']
    cpu_thread_id = module.params['cpu_thread_id']
    cpu_cpu_id = module.params['cpu_cpu_id']

    # Instantiate API connection
    api_url = module.params['api_url']
    api_auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, api_auth, api_ssl_verify)

    # Fetch list of cpus
    cpus = apiconnection.listObjects('cpu')

    # Build dict with desired state
    desired_properties = {
        'enabled': cpu_enabled,
        'process_id': cpu_process_id,
        'thread_id': cpu_thread_id,
        'cpu_id': ','.join(cpu_cpu_id)
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if cpu object with specified name exists
    for cpu in cpus:
        if cpu['name'] == cpu_name:
            uuid = cpu['uuid']
            break
    cpu_exists = (uuid != '')
    if cpu_state == 'present':
        if cpu_exists:
            cpu = apiconnection.getObjectByName('cpu', cpu_name)
            for prop in desired_properties.keys():
                # Special cases for complex propertierts:
                if prop == 'process_id':
                    current_process_id = apiconnection.getSelected(cpu[prop])
                    if current_process_id != cpu_process_id:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_process_id, desired_properties[prop]))
                elif prop == 'thread_id':
                    current_thread_id = apiconnection.getSelected(cpu[prop])
                    if current_thread_id != cpu_thread_id:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_thread_id, desired_properties[prop]))
                elif prop == 'cpu_id':
                    current_cpu_id = apiconnection.getSelectedList(cpu[prop], retval='key')
                    if not apiconnection.compareLists(current_cpu_id, cpu_cpu_id):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_cpu_id, desired_properties[prop]))
                else:
                    # catch all other properties
                    if cpu[prop] != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, cpu[prop], desired_properties[prop]))

            if not needs_change:
                result = {'changed': False, 'msg': ['Cpu already present: %s' %cpu_name, additional_msg]}
            else:
                if not module.check_mode:
                    additional_msg.append(apiconnection.updateObject('cpu', cpu_name, changed_properties))
                    if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
                result = {'changed': True, 'msg': ['Cpu %s must be changed.' %cpu_name, additional_msg]}
        else:
            if not module.check_mode:
                additional_msg.append(apiconnection.createObject('cpu', cpu_name, desired_properties))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['Cpu %s must be created.' %cpu_name, additional_msg]}
    else:
        if cpu_exists:
            if not module.check_mode:
                additional_msg.append(apiconnection.deleteObject('cpu', cpu_name))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['Cpu %s must be deleted.' %cpu_name, additional_msg]}
        else:
            result = {'changed': False, 'msg': ['Cpu %s is not present.' %cpu_name]}

    module.exit_json(**result)
Esempio n. 5
0
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            healthcheck_state=dict(type='str',
                                   choices=['present', 'absent'],
                                   default='present'),
            healthcheck_name=dict(type='str', required=True),
            healthcheck_type=dict(type='str',
                                  choices=[
                                      'tcp', 'http', 'agent', 'ldap', 'mysql',
                                      'pgsql', 'redis', 'smtp', 'esmtp', 'ssl'
                                  ],
                                  default='http'),
            healthcheck_description=dict(type='str', default=''),
            healthcheck_interval=dict(type='str', default='2s'),
            healthcheck_force_ssl=dict(type='bool', default=False),
            healthcheck_checkport=dict(type='str', default=''),
            healthcheck_http_method=dict(type='str', default='options'),
            healthcheck_http_uri=dict(type='str', default='/'),
            healthcheck_http_version=dict(type=str,
                                          choices=['http10', 'http11'],
                                          default='http10'),
            healthcheck_http_host=dict(type='str', default='localhost'),
            healthcheck_http_expression_enabled=dict(type='bool',
                                                     default=False),
            healthcheck_http_expression=dict(
                type='str',
                choices=['', 'status', 'rstatus', 'string', 'rstring'],
                default=''),
            healthcheck_http_negate=dict(type='bool', default=False),
            healthcheck_http_value=dict(type='str', default=''),
            healthcheck_tcp_enabled=dict(type='bool', default=False),
            healthcheck_tcp_send_value=dict(type='str', default=''),
            healthcheck_tcp_match_type=dict(
                type='str',
                choices=['', 'string', 'rstring', 'binary'],
                default=''),
            healthcheck_tcp_negate=dict(type='bool', default=False),
            healthcheck_tcp_match_value=dict(type='str', default=''),
            healthcheck_agent_port=dict(type='str', default=''),
            healthcheck_mysql_user=dict(type='str', default=''),
            healthcheck_mysql_post41=dict(type='bool', default=False),
            healthcheck_pgsql_user=dict(type='str', default=''),
            healthcheck_smtp_domain=dict(type='str', default=''),
            healthcheck_esmtp_domain=dict(type='str', default=''),
            healthcheck_db_user=dict(type='str', default=''),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of healthcheck
    healthcheck_state = module.params['healthcheck_state']
    healthcheck_name = module.params['healthcheck_name']
    healthcheck_description = module.params['healthcheck_description']
    healthcheck_type = module.params['healthcheck_type']
    healthcheck_interval = module.params['healthcheck_interval']
    healthcheck_force_ssl = module.params['healthcheck_force_ssl']
    healthcheck_checkport = module.params['healthcheck_checkport']
    healthcheck_http_method = module.params['healthcheck_http_method']
    healthcheck_http_uri = module.params['healthcheck_http_uri']
    healthcheck_http_version = module.params['healthcheck_http_version']
    healthcheck_http_host = module.params['healthcheck_http_host']
    healthcheck_http_expression = module.params['healthcheck_http_expression']
    healthcheck_http_expression_enabled = module.params[
        'healthcheck_http_expression_enabled']
    healthcheck_http_negate = module.params['healthcheck_http_negate']
    healthcheck_http_value = module.params['healthcheck_http_value']
    healthcheck_tcp_enabled = module.params['healthcheck_tcp_enabled']
    healthcheck_tcp_send_value = module.params['healthcheck_tcp_send_value']
    healthcheck_tcp_match_type = module.params['healthcheck_tcp_match_type']
    healthcheck_tcp_negate = module.params['healthcheck_tcp_negate']
    healthcheck_tcp_match_value = module.params['healthcheck_tcp_match_value']
    healthcheck_agent_port = module.params['healthcheck_agent_port']
    healthcheck_mysql_user = module.params['healthcheck_mysql_user']
    healthcheck_mysql_post41 = module.params['healthcheck_mysql_post41']
    healthcheck_pgsql_user = module.params['healthcheck_pgsql_user']
    healthcheck_smtp_domain = module.params['healthcheck_smtp_domain']
    healthcheck_esmtp_domain = module.params['healthcheck_esmtp_domain']
    healthcheck_db_user = module.params['healthcheck_db_user']

    # Instantiate API connection
    api_url = module.params['api_url']
    api_auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, api_auth, api_ssl_verify)

    # Fetch list of healthchecks
    healthchecks = apiconnection.listObjects('healthcheck')

    # Build dict with desired state
    desired_properties = {
        'description': healthcheck_description,
        'type': healthcheck_type,
        'interval': healthcheck_interval,
        'force_ssl': str(int(healthcheck_force_ssl)),
        'checkport': healthcheck_checkport,
        'http_method': healthcheck_http_method,
        'http_uri': healthcheck_http_uri,
        'http_version': healthcheck_http_version,
        'http_host': healthcheck_http_host,
        'http_expressionEnabled':
        str(int(healthcheck_http_expression_enabled)),
        'http_expression': healthcheck_http_expression,
        'http_negate': str(int(healthcheck_http_negate)),
        'http_value': healthcheck_http_value,
        'tcp_enabled': str(int(healthcheck_tcp_enabled)),
        'tcp_sendValue': healthcheck_tcp_send_value,
        'tcp_matchType': healthcheck_tcp_match_type,
        'tcp_negate': str(int(healthcheck_tcp_negate)),
        'tcp_matchValue': healthcheck_tcp_match_value,
        'agent_port': healthcheck_agent_port,
        'mysql_user': healthcheck_mysql_user,
        'mysql_post41': str(int(healthcheck_mysql_post41)),
        'pgsql_user': healthcheck_pgsql_user,
        'smtp_domain': healthcheck_smtp_domain,
        'esmtp_domain': healthcheck_esmtp_domain,
        'agentPort': healthcheck_agent_port,
        'smtpDomain': healthcheck_smtp_domain
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if healthcheck object with specified name exists
    for healthcheck in healthchecks:
        if healthcheck['name'] == healthcheck_name:
            uuid = healthcheck['uuid']
            break
    healthcheck_exists = (uuid != '')
    if healthcheck_state == 'present':
        if healthcheck_exists:
            healthcheck = apiconnection.getObjectByName(
                'healthcheck', healthcheck_name)
            for prop in desired_properties.keys():
                # Special cases for complex propertierts:
                if prop == 'type':
                    current_type = apiconnection.getSelected(healthcheck[prop])
                    if current_type != healthcheck_type:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_type, desired_properties[prop]))
                elif prop == 'http_method':
                    current_http_method = apiconnection.getSelected(
                        healthcheck[prop])
                    if current_http_method != healthcheck_http_method:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_http_method,
                                               desired_properties[prop]))
                elif prop == 'http_version':
                    current_http_version = apiconnection.getSelected(
                        healthcheck[prop])
                    if current_http_version != healthcheck_http_version:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_http_version,
                                               desired_properties[prop]))
                elif prop == 'http_expression':
                    current_http_expression = apiconnection.getSelected(
                        healthcheck[prop])
                    if current_http_expression != healthcheck_http_expression:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_http_expression,
                                               desired_properties[prop]))
                elif prop == 'tcp_matchType':
                    current_tcp_match_type = apiconnection.getSelected(
                        healthcheck[prop])
                    if current_tcp_match_type != healthcheck_tcp_match_type:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_tcp_match_type,
                                               desired_properties[prop]))
                else:
                    # catch all other properties
                    if healthcheck[prop] != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, healthcheck[prop],
                                               desired_properties[prop]))

            if not needs_change:
                result = {
                    'changed':
                    False,
                    'msg': [
                        'Healthcheck already present: %s' % healthcheck_name,
                        additional_msg
                    ]
                }
            else:
                if not module.check_mode:
                    additional_msg.append(
                        apiconnection.updateObject('healthcheck',
                                                   healthcheck_name,
                                                   changed_properties))
                    if haproxy_reload:
                        additional_msg.append(apiconnection.applyConfig())
                result = {
                    'changed':
                    True,
                    'msg': [
                        'Healthcheck %s must be changed.' % healthcheck_name,
                        additional_msg
                    ]
                }
        else:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.createObject('healthcheck', healthcheck_name,
                                               desired_properties))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg': [
                    'Healthcheck %s must be created.' % healthcheck_name,
                    additional_msg
                ]
            }
    else:
        if healthcheck_exists:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.deleteObject('healthcheck',
                                               healthcheck_name))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg': [
                    'Healthcheck %s must be deleted.' % healthcheck_name,
                    additional_msg
                ]
            }
        else:
            result = {
                'changed': False,
                'msg': ['Healthcheck %s is not present.' % healthcheck_name]
            }

    module.exit_json(**result)
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            server_enabled=dict(type='bool', default=True),
            server_name=dict(type='str', required=True),
            server_address=dict(type='str', required=True),
            server_description=dict(type='str', default=''),
            server_port=dict(type='str', required=True),
            server_checkport=dict(type='str', default=''),
            server_mode=dict(type='str', default='active'),
            server_ssl=dict(type='bool', default=False),
            server_ssl_verify=dict(type='bool', default=True),
            server_ssl_ca=dict(type='list', default=[]),
            server_ssl_crl=dict(type='str', default=''),
            server_ssl_client_certificate=dict(type='str', default=''),
            server_weight=dict(type='str', default=''),
            server_check_interval=dict(type='str', default=''),
            server_check_down_interval=dict(type='str', default=''),
            server_source=dict(type='str', default=''),
            server_advanced=dict(type='str', default=''),
            server_state=dict(type='str',
                              choices=['present', 'absent'],
                              default='present'),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of server
    server_state = module.params['server_state']
    server_enabled = module.params['server_enabled']
    server_name = module.params['server_name']
    server_description = module.params['server_description']
    server_address = module.params['server_address']
    server_port = module.params['server_port']
    server_checkport = module.params['server_port']
    server_mode = module.params['server_mode']
    server_ssl = module.params['server_ssl']
    server_ssl_verify = module.params['server_ssl_verify']
    server_ssl_ca = module.params['server_ssl_ca']
    server_ssl_crl = module.params['server_ssl_crl']
    server_ssl_client_certificate = module.params[
        'server_ssl_client_certificate']
    server_weight = module.params['server_weight']
    server_check_interval = module.params['server_check_interval']
    server_check_down_interval = module.params['server_check_down_interval']
    server_source = module.params['server_source']
    server_advanced = module.params['server_advanced']
    # Instantiate API connection
    api_url = module.params['api_url']
    auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, auth, api_ssl_verify)

    # Fetch list of servers
    servers = apiconnection.listObjects('server')

    # Build dict with desired state
    desired_properties = {
        'name': server_name,
        'address': server_address,
        'description': server_description,
        'port': server_port,
        'checkport': server_checkport,
        'mode': server_mode,
        'ssl': str(int(server_ssl)),
        'sslVerify': str(int(server_ssl_verify)),
        'sslCA': server_ssl_ca,
        'sslCRL': server_ssl_crl,
        'sslClientCertificate': server_ssl_client_certificate,
        'weight': server_weight,
        'checkInterval': server_check_interval,
        'checkDownInterval': server_check_down_interval,
        'source': server_source,
        'advanced': server_advanced,
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if server object with specified name exists
    for server in servers:
        if server['name'] == server_name:
            server_exists = True
            uuid = server['uuid']
            break
    server_exists = (uuid != '')

    if server_state == 'present':
        if server_exists:
            server = apiconnection.getObjectByName('server', server_name)
            #for prop in ['code', 'content', 'description']:
            for prop in desired_properties.keys():
                # Special case for mode where selected element must be determined
                if prop == 'mode':
                    current_mode = apiconnection.getSelected(server[prop])
                    if current_mode != server_mode:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing mode: %s => %s' %
                                              (current_mode, server_mode))
                # Special case for sslCA where a list must be compared to a dict
                elif prop == 'sslCA':
                    # Get current list of selected CAs by their name
                    current_ssl_ca = apiconnection.getSelectedList(
                        server[prop])
                    # Build list with keys of desired CAs
                    desired_ssl_ca = []
                    # Search the ids of desired ssl_ca names
                    for ca in server_ssl_ca:
                        ca_key = apiconnection.findValueInDict(server[prop],
                                                               searchvalue=ca)
                        if ca_key != '':
                            desired_ssl_ca.append(ca_key)
                    # Compare current and desired list of CA names
                    if not apiconnection.compareLists(current_ssl_ca,
                                                      server_ssl_ca):
                        needs_change = True
                        changed_properties['sslCA'] = ','.join(desired_ssl_ca)
                        additional_msg.append('Changing sslCA: %s => %s' %
                                              (current_ssl_ca, desired_ssl_ca))
                # Special case for sslCRL where a list must be compared to a dict
                elif prop == 'sslCRL':
                    current_ssl_crl = apiconnection.getSelected(server[prop])
                    # Find id of sslCRL with desired name
                    desired_ssl_crl = apiconnection.findValueInDict(
                        server[prop], searchvalue=server_ssl_crl)
                    if not current_ssl_crl == desired_ssl_crl:
                        needs_change = True
                        changed_properties['sslCRL'] = desired_ssl_crl
                        additional_msg.append(
                            'Changing sslCRL: %s => %s' %
                            (current_ssl_crl, server_ssl_crl))
                # Special case for sslClientCertificate where a single object must be determined from a list
                elif prop == 'sslClientCertificate':
                    current_ssl_client_certificate = apiconnection.getSelected(
                        server[prop], retval='value')
                    if current_ssl_client_certificate != server_ssl_client_certificate:
                        needs_change = True
                        desired_ssl_client_certificate = apiconnection.findValueInDict(
                            server[prop],
                            searchvalue=server_ssl_client_certificate)
                        changed_properties[
                            'sslClientCertificate'] = desired_ssl_client_certificate
                        additional_msg.append(
                            'Changing sslClientCertificate: %s => %s' %
                            (current_ssl_client_certificate,
                             server_ssl_client_certificate))
                # Standard case for regular simple values
                else:
                    if server[prop] != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, server[prop], desired_properties[prop]))
            if not needs_change:
                result = {
                    'changed':
                    False,
                    'msg': [
                        'Server already present: %s' % server_name,
                        additional_msg
                    ]
                }
            else:
                if not module.check_mode:
                    additional_msg.append(
                        apiconnection.updateObject('server', server_name,
                                                   changed_properties))
                    if haproxy_reload:
                        additional_msg.append(apiconnection.applyConfig())
                result = {
                    'changed':
                    True,
                    'msg': [
                        'Server %s must be changed.' % server_name,
                        additional_msg
                    ]
                }
        else:
            if not module.check_mode:
                # clear parameters sslCA, sslCRL and sslClientCertificate, because we can't get their available ids at this time
                desired_properties['sslCA'] = ''
                desired_properties['sslCRL'] = ''
                desired_properties['sslClientCertificate'] = ''
                additional_msg.append(
                    apiconnection.createObject('server', server_name,
                                               desired_properties))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed': True,
                'msg':
                ['Server %s must be created.' % server_name, additional_msg]
            }
    else:
        if server_exists:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.deleteObject('server', server_name))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed': True,
                'msg':
                ['Server %s must be deleted.' % server_name, additional_msg]
            }
        else:
            result = {
                'changed': False,
                'msg': ['Server %s is not present.' % server_name]
            }

    module.exit_json(**result)
Esempio n. 7
0
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            mapfile_name=dict(type='str', required=True),
            mapfile_description=dict(type='str', default=''),
            mapfile_content=dict(type='str', default=''),
            mapfile_state=dict(type='str',
                               choices=['present', 'absent'],
                               default='present'),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of mapfile
    mapfile_name = module.params['mapfile_name']
    mapfile_content = module.params['mapfile_content']
    mapfile_state = module.params['mapfile_state']
    mapfile_description = module.params['mapfile_description']
    # Instantiate API connection
    api_url = module.params['api_url']
    api_auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, api_auth, api_ssl_verify)

    # Fetch list of mapfiles
    mapfiles = apiconnection.listObjects('mapfile')

    # Build dict with desired state
    desired_properties = {
        'description': mapfile_description,
        'content': mapfile_content
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if mapfile object with specified name exists
    for mapfile in mapfiles:
        if mapfile['name'] == mapfile_name:
            uuid = mapfile['uuid']
            break
    mapfile_exists = (uuid != '')

    if mapfile_state == 'present':
        if mapfile_exists:
            mapfile = apiconnection.getObjectByName('mapfile', mapfile_name)
            for prop in desired_properties.keys():
                if mapfile[prop] != desired_properties[prop]:
                    needs_change = True
                    changed_properties[prop] = desired_properties[prop]
            if not needs_change:
                result = {
                    'changed': False,
                    'msg': ['Mapfile already present: %s' % mapfile_name]
                }
            else:
                if not module.check_mode:
                    additional_msg.append(
                        apiconnection.updateObject('mapfile', mapfile_name,
                                                   changed_properties))
                    if haproxy_reload:
                        additional_msg.append(apiconnection.applyConfig())
                result = {
                    'changed':
                    True,
                    'msg': [
                        'Mapfile %s must be changed.' % mapfile_name,
                        additional_msg
                    ]
                }
        else:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.createObject('mapfile', mapfile_name,
                                               desired_properties))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg':
                ['Mapfile %s must be created.' % mapfile_name, additional_msg]
            }
    else:
        if mapfile_exists:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.deleteObject('mapfile', mapfile_name))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg':
                ['Mapfile %s must be deleted.' % mapfile_name, additional_msg]
            }
        else:
            result = {
                'changed': False,
                'msg': ['Mapfile %s is not present.' % mapfile_name]
            }

    module.exit_json(**result)
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            backend_state=dict(type='str',
                               choices=['present', 'absent'],
                               default='present'),
            backend_enabled=dict(type='bool', default=True),
            backend_name=dict(type='str', required=True),
            backend_description=dict(type='str', default=''),
            backend_mode=dict(type='str',
                              choices=['http', 'tcp'],
                              default='http'),
            backend_algorithm=dict(type='str',
                                   choices=[
                                       'source', 'roundrobin', 'static-rr',
                                       'leastconn', 'uri'
                                   ],
                                   default='source'),
            backend_proxy_protocol=dict(type='str',
                                        choices=['', 'v1', 'v2'],
                                        default=''),
            backend_linked_servers=dict(type='list', default=[]),
            backend_source=dict(type='str', default=''),
            backend_health_check_enabled=dict(type='bool', default=True),
            backend_health_check=dict(type='str', default=''),
            backend_health_check_log_status=dict(type='bool', default=False),
            backend_check_interval=dict(type='str', default=''),
            backend_check_down_interval=dict(type='str', default=''),
            backend_health_check_fall=dict(type='str', default=''),
            backend_health_check_rise=dict(type='str', default=''),
            backend_persistence=dict(type='str',
                                     choices=['', 'sticktable', 'cookie'],
                                     default='sticktable'),
            backend_persistence_cookie_mode=dict(type='str',
                                                 choices=['piggyback', 'new'],
                                                 default='piggyback'),
            backend_persistence_cookie_name=dict(type='str',
                                                 default='SRVCOOKIE'),
            backend_persistence_strip_quotes=dict(type='bool', default=True),
            backend_stickiness_pattern=dict(type='str',
                                            choices=[
                                                '', 'sourceipv4', 'sourceipv6',
                                                'cookievalue', 'rdpcookie'
                                            ],
                                            default='sourceipv4'),
            backend_stickiness_data_types=dict(
                type='list',
                elements='str',
                options=dict(choices=[
                    'conn_cnt', 'conn_cur', 'conn_rate', 'sess_cnt',
                    'sess_rate', 'http_req_cnt', 'http_req_rate',
                    'http_err_cnt', 'http_err_rate', 'bytes_in_cnt',
                    'bytes_in_rate', 'bytes_out_cnt', 'bytes_out_rate'
                ], ),
                default=[]),
            backend_stickiness_expire=dict(type='str', default='30m'),
            backend_stickiness_size=dict(type='str', default='50k'),
            backend_stickiness_cookie_name=dict(type='str', default=''),
            backend_stickiness_cookie_length=dict(type='str', default=''),
            backend_stickiness_conn_rate_period=dict(type='str',
                                                     default='10s'),
            backend_stickiness_sess_rate_period=dict(type='str',
                                                     default='10s'),
            backend_stickiness_http_req_rate_period=dict(type='str',
                                                         default='10s'),
            backend_stickiness_http_err_rate_period=dict(type='str',
                                                         default='10s'),
            backend_stickiness_bytes_in_rate_period=dict(type='str',
                                                         default='1m'),
            backend_stickiness_bytes_out_rate_period=dict(type='str',
                                                          default='1m'),
            backend_basic_auth_enabled=dict(type='bool', default=False),
            backend_basic_auth_users=dict(type='list', default=[]),
            backend_basic_auth_groups=dict(type='list', default=[]),
            backend_tuning_timeout_connect=dict(type='str', default=''),
            backend_tuning_timeout_check=dict(type='str', default=''),
            backend_tuning_timeout_server=dict(type='str', default=''),
            backend_tuning_retries=dict(type='str', default=''),
            backend_custom_options=dict(type='str', default=''),
            backend_tuning_default_server=dict(type='str', default=''),
            backend_tuning_no_port=dict(type='bool', default=False),
            backend_tuning_http_reuse=dict(
                type='str',
                choices=['', 'never', 'safe', 'aggressive', 'always'],
                default='never'),
            backend_linked_actions=dict(type='list', default=[]),
            backend_linked_errorfiles=dict(type='list', default=[]),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of backend
    backend_state = module.params['backend_state']
    backend_enabled = str(int(module.params['backend_enabled']))
    backend_name = module.params['backend_name']
    backend_description = module.params['backend_description']
    backend_mode = module.params['backend_mode']
    backend_algorithm = module.params['backend_algorithm']
    backend_proxy_protocol = module.params['backend_proxy_protocol']
    backend_linked_servers = module.params['backend_linked_servers']
    backend_source = module.params['backend_source']
    backend_health_check_enabled = module.params[
        'backend_health_check_enabled']
    backend_health_check = module.params['backend_health_check']
    backend_health_check_log_status = module.params[
        'backend_health_check_log_status']
    backend_check_interval = module.params['backend_check_interval']
    backend_check_down_interval = module.params['backend_check_down_interval']
    backend_health_check_fall = module.params['backend_health_check_fall']
    backend_health_check_rise = module.params['backend_health_check_rise']
    backend_persistence = module.params['backend_persistence']
    backend_persistence_cookie_mode = module.params[
        'backend_persistence_cookie_mode']
    backend_persistence_cookie_name = module.params[
        'backend_persistence_cookie_name']
    backend_persistence_strip_quotes = module.params[
        'backend_persistence_strip_quotes']
    backend_stickiness_pattern = module.params['backend_stickiness_pattern']
    backend_stickiness_data_types = module.params[
        'backend_stickiness_data_types']
    backend_stickiness_expire = module.params['backend_stickiness_expire']
    backend_stickiness_size = module.params['backend_stickiness_size']
    backend_stickiness_cookie_name = module.params[
        'backend_stickiness_cookie_name']
    backend_stickiness_cookie_length = module.params[
        'backend_stickiness_cookie_length']
    backend_stickiness_conn_rate_period = module.params[
        'backend_stickiness_conn_rate_period']
    backend_stickiness_sess_rate_period = module.params[
        'backend_stickiness_sess_rate_period']
    backend_stickiness_http_req_rate_period = module.params[
        'backend_stickiness_http_req_rate_period']
    backend_stickiness_http_err_rate_period = module.params[
        'backend_stickiness_http_err_rate_period']
    backend_stickiness_bytes_in_rate_period = module.params[
        'backend_stickiness_bytes_in_rate_period']
    backend_stickiness_bytes_out_rate_period = module.params[
        'backend_stickiness_bytes_out_rate_period']
    backend_basic_auth_enabled = module.params['backend_basic_auth_enabled']
    backend_basic_auth_users = module.params['backend_basic_auth_users']
    backend_basic_auth_groups = module.params['backend_basic_auth_groups']
    backend_tuning_timeout_connect = module.params[
        'backend_tuning_timeout_connect']
    backend_tuning_timeout_check = module.params[
        'backend_tuning_timeout_check']
    backend_tuning_timeout_server = module.params[
        'backend_tuning_timeout_server']
    backend_tuning_retries = module.params['backend_tuning_retries']
    backend_custom_options = module.params['backend_custom_options']
    backend_tuning_default_server = module.params[
        'backend_tuning_default_server']
    backend_tuning_no_port = module.params['backend_tuning_no_port']
    backend_tuning_http_reuse = module.params['backend_tuning_http_reuse']
    backend_linked_actions = module.params['backend_linked_actions']
    backend_linked_errorfiles = module.params['backend_linked_errorfiles']

    # Instantiate API connection
    api_url = module.params['api_url']
    api_auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, api_auth, api_ssl_verify)

    # Fetch list of backends
    backends = apiconnection.listObjects('backend')
    # Get an empty backend object to lookup UUIDs for:
    # - linkedServers
    # - healthCheck
    # - basicAuthUsers
    # - basicAuthGroups
    # - linkedActions
    # - linkedErrorfiles
    empty_backend = apiconnection.getObjectByUuid('backend', '')
    # Need to get UUIDs for:
    # linkedServers, healthCheck, basicAuthUsers, basicAuthGroups, linkedActions, linkedErrorfiles
    backend_linked_servers_uuids = []
    #backend_linked_servers_uuids = apiconnection.getUuidsFromNames('server', backend_linked_servers)
    for backend_linked_server in backend_linked_servers:
        for key, value in empty_backend['linkedServers'].iteritems():
            if value['value'] == backend_linked_server:
                backend_linked_servers_uuids.append(key)
    backend_health_check_uuid = ''
    if backend_health_check != '':
        #backend_health_check_uuid = apiconnection.getUuidByName('healthcheck', backend_health_check)
        for key, value in empty_backend['healthCheck'].iteritems():
            if value['value'] == backend_health_check:
                backend_health_check_uuid = key
    #backend_basic_auth_users_uuids = apiconnection.getUuidsFromNames('user', backend_basic_auth_users)
    backend_basic_auth_users_uuids = []
    for backend_basic_auth_user in backend_basic_auth_users:
        for key, value in empty_backend['basicAuthUsers'].iteritems():
            if value['value'] == backend_basic_auth_user:
                backend_basic_auth_users_uuids.append(key)
    #backend_basic_auth_groups_uuids = apiconnection.getUuidsFromNames('group', backend_basic_auth_groups)
    backend_basic_auth_groups_uuids = []
    for backend_basic_auth_group in backend_basic_auth_groups:
        for key, value in empty_backend['basicAuthGroups'].iteritems():
            if value['value'] == backend_basic_auth_group:
                backend_basic_auth_groups_uuids.append(key)
    #backend_linked_actions_uuids = apiconnection.getUuidsFromNames('action', backend_linked_actions)
    backend_linked_actions_uuids = []
    for backend_linked_action in backend_linked_actions:
        for key, value in empty_backend['linkedActions'].iteritems():
            if value['value'] == backend_linked_action:
                backend_linked_actions_uuids.append(key)
    #backend_linked_errorfiles_uuids = apiconnection.getUuidsFromNames('errorfile', backend_linked_errorfiles)
    backend_linked_errorfiles_uuids = []
    for backend_linked_errorfile in backend_linked_errorfiles:
        for key, value in empty_backend['linkedErrorfiles'].iteritems():
            if value['value'] == backend_linked_errorfile:
                backend_linked_errorfiles_uuids.append(key)
    # Build dict with desired state
    desired_properties = {
        'enabled': backend_enabled,
        'description': backend_description,
        'mode': backend_mode,
        'algorithm': backend_algorithm,
        'proxyProtocol': backend_proxy_protocol,
        'linkedServers': ','.join(backend_linked_servers_uuids),
        'source': backend_source,
        'healthCheckEnabled': str(int(backend_health_check_enabled)),
        'healthCheck': backend_health_check_uuid,
        'healthCheckLogStatus': str(int(backend_health_check_log_status)),
        'checkInterval': backend_check_interval,
        'checkDownInterval': backend_check_down_interval,
        'healthCheckFall': backend_health_check_fall,
        'healthCheckRise': backend_health_check_rise,
        'persistence': backend_persistence,
        'persistence_cookiemode': backend_persistence_cookie_mode,
        'persistence_cookiename': backend_persistence_cookie_name,
        'persistence_stripquotes': str(int(backend_persistence_strip_quotes)),
        'stickiness_pattern': backend_stickiness_pattern,
        'stickiness_dataTypes': ','.join(backend_stickiness_data_types),
        'stickiness_expire': backend_stickiness_expire,
        'stickiness_size': backend_stickiness_size,
        'stickiness_cookiename': backend_stickiness_cookie_name,
        'stickiness_cookielength': backend_stickiness_cookie_length,
        'stickiness_connRatePeriod': backend_stickiness_conn_rate_period,
        'stickiness_sessRatePeriod': backend_stickiness_sess_rate_period,
        'stickiness_httpReqRatePeriod':
        backend_stickiness_http_req_rate_period,
        'stickiness_httpErrRatePeriod':
        backend_stickiness_http_err_rate_period,
        'stickiness_bytesInRatePeriod':
        backend_stickiness_bytes_in_rate_period,
        'stickiness_bytesOutRatePeriod':
        backend_stickiness_bytes_out_rate_period,
        'basicAuthEnabled': str(int(backend_basic_auth_enabled)),
        'basicAuthUsers': ','.join(backend_basic_auth_users_uuids),
        'basicAuthGroups': ','.join(backend_basic_auth_groups_uuids),
        'tuning_timeoutConnect': backend_tuning_timeout_connect,
        'tuning_timeoutCheck': backend_tuning_timeout_check,
        'tuning_timeoutServer': backend_tuning_timeout_server,
        'tuning_retries': backend_tuning_retries,
        'customOptions': backend_custom_options,
        'tuning_defaultserver': backend_tuning_default_server,
        'tuning_noport': str(int(backend_tuning_no_port)),
        'tuning_httpreuse': backend_tuning_http_reuse,
        'linkedActions': ','.join(backend_linked_actions_uuids),
        'linkedErrorfiles': ','.join(backend_linked_errorfiles_uuids)
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if backend object with specified name exists
    for backend in backends:
        if backend['name'] == backend_name:
            uuid = backend['uuid']
            break
    backend_exists = (uuid != '')
    if backend_state == 'present':
        if backend_exists:
            backend = apiconnection.getObjectByName('backend', backend_name)
            for prop in desired_properties.keys():
                # Special cases for complex propertierts:
                if prop == 'mode':
                    current_mode = apiconnection.getSelected(backend[prop])
                    if current_mode != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_mode, desired_properties[prop]))
                elif prop == 'algorithm':
                    current_algorithm = apiconnection.getSelected(
                        backend[prop])
                    if current_algorithm != backend_algorithm:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_algorithm,
                                               desired_properties[prop]))
                elif prop == 'proxyProtocol':
                    current_proxy_protocol = apiconnection.getSelected(
                        backend[prop])
                    if current_proxy_protocol != backend_proxy_protocol:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_proxy_protocol,
                                               desired_properties[prop]))
                elif prop == 'linkedServers':
                    current_linked_servers = apiconnection.getSelectedList(
                        backend[prop], retval='key')
                    if not apiconnection.compareLists(
                            current_linked_servers,
                            backend_linked_servers_uuids):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_linked_servers,
                                               desired_properties[prop]))
                elif prop == 'healthCheck':
                    current_health_check = apiconnection.getSelected(
                        backend[prop], retval='value')
                    if current_health_check != backend_health_check:
                        if current_health_check == 'none' and backend_health_check == '':
                            pass
                        else:
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append('Changing %s: %s => %s' %
                                                  (prop, current_health_check,
                                                   desired_properties[prop]))
                elif prop == 'persistence':
                    current_persistence = apiconnection.getSelected(
                        backend[prop], retval='key')
                    if current_persistence != backend_persistence:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_persistence,
                                               desired_properties[prop]))
                elif prop == 'persistence_cookiemode':
                    current_persistence_cookie_mode = apiconnection.getSelected(
                        backend[prop], retval='key')
                    if current_persistence_cookie_mode != backend_persistence_cookie_mode:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_persistence_cookie_mode,
                             desired_properties[prop]))
                elif prop == 'stickiness_pattern':
                    current_stickiness_pattern = apiconnection.getSelected(
                        backend[prop])
                    if current_stickiness_pattern != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_stickiness_pattern,
                             desired_properties[prop]))
                elif prop == 'stickiness_dataTypes':
                    current_stickiness_data_types = apiconnection.getSelected(
                        backend[prop])
                    if not apiconnection.compareLists(
                            current_stickiness_data_types,
                            backend_stickiness_data_types):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_stickiness_data_types,
                             desired_properties[prop]))
                elif prop == 'basicAuthUsers':
                    # only care about basicAuthUsers when basic auth is enabled
                    if backend_basic_auth_enabled:
                        current_basic_auth_users = apiconnection.getSelectedList(
                            backend[prop])
                        if not apiconnection.compareLists(
                                current_basic_auth_users,
                                backend_basic_auth_users):
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_basic_auth_users,
                                 desired_properties[prop]))
                elif prop == 'basicAuthGroups':
                    # only care about basicAuthGroups when basic auth is enabled
                    if backend_basic_auth_enabled:
                        current_basic_auth_groups = apiconnection.getSelectedList(
                            backend[prop])
                        if not apiconnection.compareLists(
                                current_basic_auth_groups,
                                backend_basic_auth_groups):
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_basic_auth_groups,
                                 desired_properties[prop]))
                elif prop == 'tuning_httpreuse':
                    current_tuning_httpreuse = apiconnection.getSelected(
                        backend[prop])
                    if current_tuning_httpreuse != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_tuning_httpreuse,
                                               desired_properties[prop]))
                elif prop == 'linkedActions':
                    current_linked_actions = apiconnection.getSelectedList(
                        backend[prop], retval='key')
                    if not apiconnection.compareLists(
                            current_linked_actions,
                            backend_linked_actions_uuids,
                            order_sensitive=True):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_linked_actions,
                                               desired_properties[prop]))
                elif prop == 'linkedErrorfiles':
                    current_linked_errorfiles = apiconnection.getSelectedList(
                        backend[prop], retval='key')
                    if not apiconnection.compareLists(
                            current_linked_errorfiles,
                            backend_linked_errorfiles_uuids):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_linked_errorfiles,
                                               desired_properties[prop]))
                else:
                    # catch all other properties
                    if backend[prop] != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, backend[prop], desired_properties[prop]))

            if not needs_change:
                result = {
                    'changed':
                    False,
                    'msg': [
                        'Backend already present: %s' % backend_name,
                        additional_msg
                    ]
                }
            else:
                if not module.check_mode:
                    # workaround for https://github.com/opnsense/plugins/issues/1494
                    # any change must include the linkedActions to maintain the correct order
                    changed_properties['linkedActions'] = desired_properties[
                        'linkedActions']
                    additional_msg.append(
                        apiconnection.updateObject('backend', backend_name,
                                                   changed_properties))
                    if haproxy_reload:
                        additional_msg.append(apiconnection.applyConfig())
                result = {
                    'changed':
                    True,
                    'msg': [
                        'Backend %s must be changed.' % backend_name,
                        additional_msg
                    ]
                }
        else:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.createObject('backend', backend_name,
                                               desired_properties))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg':
                ['Backend %s must be created.' % backend_name, additional_msg]
            }
    else:
        if backend_exists:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.deleteObject('backend', backend_name))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg':
                ['Backend %s must be deleted.' % backend_name, additional_msg]
            }
        else:
            result = {
                'changed': False,
                'msg': ['Backend %s is not present.' % backend_name]
            }

    module.exit_json(**result)
Esempio n. 9
0
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            acl_state=dict(type='str', choices=['present', 'absent'], default='present'),
            acl_name=dict(type='str', required=True),
            acl_description=dict(type='str', default=''),
            acl_expression=dict(type='str',
                choices=[
                    'http_auth',
                    'hdr_beg',
                    'hdr_end',
                    'hdr',
                    'hdr_reg',
                    'hdr_sub',
                    'path_beg',
                    'path_end',
                    'path',
                    'path_reg',
                    'path_dir',
                    'path_sub',
                    'url_param',
                    'ssl_c_verify',
                    'ssl_c_verify_code',
                    'ssl_c_ca_commonname',
                    'src',
                    'src_is_local',
                    'src_port',
                    'src_bytes_in_rate',
                    'src_bytes_out_rate',
                    'src_kbytes_in',
                    'src_kbytes_out',
                    'src_conn_cnt',
                    'src_conn_cur',
                    'src_conn_rate',
                    'src_http_err_cnt',
                    'src_http_err_rate',
                    'src_http_req_cnt',
                    'src_http_req_rate',
                    'src_sess_rate',
                    'nbsrv',
                    'traffic_is_http',
                    'traffic_is_ssl',
                    'ssl_fc',
                    'ssl_fc_sni',
                    'ssl_sni',
                    'ssl_sni_sub',
                    'ssl_sni_beg',
                    'ssl_sni_end',
                    'ssl_sni_reg',
                    'custom_acl'
                ], required=True),
            acl_negate=dict(type='bool', default=False),
            acl_hdr_beg=dict(type='str', default=''),
            acl_hdr_end=dict(type='str', default=''),
            acl_hdr=dict(type='str', default=''),
            acl_hdr_reg=dict(type='str', default=''),
            acl_hdr_sub=dict(type='str', default=''),
            acl_path_beg=dict(type='str', default=''),
            acl_path_end=dict(type='str', default=''),
            acl_path=dict(type='str', default=''),
            acl_path_reg=dict(type='str', default=''),
            acl_path_dir=dict(type='str', default=''),
            acl_path_sub=dict(type='str', default=''),
            acl_url_param=dict(type='str', default=''),
            acl_url_param_value=dict(type='str', default=''),
            acl_ssl_c_verify_code=dict(type='str', default=''),
            acl_ssl_c_ca_commonname=dict(type='str', default=''),
            acl_src=dict(type='str', default=''),
            acl_src_bytes_in_rate_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_bytes_in_rate=dict(type='str', default=''),
            acl_src_bytes_out_rate_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_bytes_out_rate=dict(type='str', default=''),
            acl_src_conn_cnt_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_conn_cnt=dict(type='str', default=''),
            acl_src_conn_rate_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_conn_rate=dict(type='str', default=''),
            acl_src_http_err_cnt_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_http_err_cnt=dict(type='str', default=''),
            acl_src_http_err_rate_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_http_err_rate=dict(type='str', default=''),
            acl_src_http_req_rate_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_http_req_rate=dict(type='str', default=''),
            acl_src_kbytes_in_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_kbytes_in=dict(type='str', default=''),
            acl_src_kbytes_out_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_kbytes_out=dict(type='str', default=''),
            acl_src_port_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_port=dict(type='str', default=''),
            acl_src_sess_cnt_comparison=dict(type='str', choices=['', 'gt', 'ge', 'eq', 'lt', 'le'], default='gt'),
            acl_src_sess_cnt=dict(type='str', default=''),
            acl_nbsrv=dict(type='str', default=''),
            acl_nbsrv_backend=dict(type='str', default=''),
            acl_ssl_fc_sni=dict(type='str', default=''),
            acl_ssl_sni=dict(type='str', default=''),
            acl_ssl_sni_sub=dict(type='str', default=''),
            acl_ssl_sni_beg=dict(type='str', default=''),
            acl_ssl_sni_end=dict(type='str', default=''),
            acl_ssl_sni_reg=dict(type='str', default=''),
            acl_custom_acl=dict(type='str', default=''),
            acl_value=dict(type='str', default=''),
            acl_query_backend=dict(type='str', default=''),
            acl_allowed_users=dict(type='list', default=[]),
            acl_allowed_groups=dict(type='list', default=[]),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    # Instantiate API connection
    api_url = module.params['api_url']
    api_auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, api_auth, api_ssl_verify)

    # Prepare properties of acl
    haproxy_reload = module.params['haproxy_reload']
    acl_state = module.params['acl_state']
    acl_name = module.params['acl_name']
    acl_description = module.params['acl_description']
    acl_expression = module.params['acl_expression']
    acl_negate = module.params['acl_negate']
    acl_hdr_beg = module.params['acl_hdr_beg']
    acl_hdr_end = module.params['acl_hdr_end']
    acl_hdr = module.params['acl_hdr']
    acl_hdr_reg = module.params['acl_hdr_reg']
    acl_hdr_sub = module.params['acl_hdr_sub']
    acl_path_beg = module.params['acl_path_beg']
    acl_path_end = module.params['acl_path_end']
    acl_path = module.params['acl_path']
    acl_path_reg = module.params['acl_path_reg']
    acl_path_dir = module.params['acl_path_dir']
    acl_path_sub = module.params['acl_path_sub']
    acl_url_param = module.params['acl_url_param']
    acl_url_param_value = module.params['acl_url_param_value']
    acl_ssl_c_verify_code = module.params['acl_ssl_c_verify_code']
    acl_ssl_c_ca_commonname = module.params['acl_ssl_c_ca_commonname']
    acl_src = module.params['acl_src']
    acl_src_bytes_in_rate_comparison = module.params['acl_src_bytes_in_rate_comparison']
    acl_src_bytes_in_rate = module.params['acl_src_bytes_in_rate']
    acl_src_bytes_out_rate_comparison = module.params['acl_src_bytes_out_rate_comparison']
    acl_src_bytes_out_rate = module.params['acl_src_bytes_out_rate']
    acl_src_conn_cnt_comparison = module.params['acl_src_conn_cnt_comparison']
    acl_src_conn_cnt = module.params['acl_src_conn_cnt']
    acl_src_conn_rate_comparison = module.params['acl_src_conn_rate_comparison']
    acl_src_conn_rate = module.params['acl_src_conn_rate']
    acl_src_http_err_cnt_comparison = module.params['acl_src_http_err_cnt_comparison']
    acl_src_http_err_cnt = module.params['acl_src_http_err_cnt']
    acl_src_http_err_rate_comparison = module.params['acl_src_http_err_rate_comparison']
    acl_src_http_err_rate = module.params['acl_src_http_err_rate']
    acl_src_http_req_rate_comparison = module.params['acl_src_http_req_rate_comparison']
    acl_src_http_req_rate = module.params['acl_src_http_req_rate']
    acl_src_kbytes_in_comparison = module.params['acl_src_kbytes_in_comparison']
    acl_src_kbytes_in = module.params['acl_src_kbytes_in']
    acl_src_kbytes_out_comparison = module.params['acl_src_kbytes_out_comparison']
    acl_src_kbytes_out = module.params['acl_src_kbytes_out']
    acl_src_port_comparison = module.params['acl_src_port_comparison']
    acl_src_port = module.params['acl_src_port']
    acl_src_sess_cnt_comparison = module.params['acl_src_sess_cnt_comparison']
    acl_src_sess_cnt = module.params['acl_src_sess_cnt']
    acl_nbsrv = module.params['acl_nbsrv']
    acl_nbsrv_backend = module.params['acl_nbsrv_backend']
    acl_ssl_fc_sni = module.params['acl_ssl_fc_sni']
    acl_ssl_sni = module.params['acl_ssl_sni']
    acl_ssl_sni_sub = module.params['acl_ssl_sni_sub']
    acl_ssl_sni_beg = module.params['acl_ssl_sni_beg']
    acl_ssl_sni_end = module.params['acl_ssl_sni_end']
    acl_ssl_sni_reg = module.params['acl_ssl_sni_reg']
    acl_custom_acl = module.params['acl_custom_acl']
    acl_value = module.params['acl_value']
    acl_query_backend = module.params['acl_query_backend']
    acl_allowed_users = module.params['acl_allowed_users']
    acl_allowed_groups = module.params['acl_allowed_groups']

    # Get empty ACL object to lookup UUIDs for allowedUsers, allowedGroups, nbsrv_backend, queryBackend
    empty_acl = apiconnection.getObjectByUuid('acl', '')
    #acl_nbsrv_backend = apiconnection.getUuidByName('backend', module.params['acl_nbsrv_backend'])
    acl_nbsrv_backend_uuid = ''
    if acl_nbsrv_backend != '':
        for key,value in empty_acl['nbsrv_backend'].iteritems():
            if value['value'] == acl_nbsrv_backend:
                acl_nbsrv_backend_uuid = key
    #acl_query_backend = apiconnection.getUuidByName('backend', module.params['acl_query_backend'])
    acl_query_backend_uuid = ''
    if acl_query_backend != '':
        for key,value in empty_acl['queryBackend'].iteritems():
            if value['value'] == acl_query_backend:
                acl_query_backend_uuid = key
    # Resolve UUIDs for users
    acl_allowed_users_uuids = []
    for acl_allowed_user in acl_allowed_users:
        #acl_allowed_users_uuids.append(apiconnection.getUuidByName('user', acl_allowed_user))
        for key,value in empty_acl['allowedUsers'].iteritems():
            if value['value'] == acl_allowed_user:
                acl_allowed_users_uuids.append(key)
    # Resolve UUIDs for allowedGroups
    acl_allowed_groups_uuids = []
    for acl_allowed_group in acl_allowed_groups:
        #acl_allowed_groups_uuids.append(apiconnection.getUuidByName('group', acl_allowed_group))
        for key,value in empty_acl['allowedGroups'].iteritems():
            if value['value'] == acl_allowed_group:
                acl_allowed_groups_uuids.append(key)

    # Fetch list of acls
    acls = apiconnection.listObjects('acl')

    # Build dict with desired state
    desired_properties = {
        'description': acl_description,
        'expression': acl_expression,
        'negate': str(int(acl_negate)),
        'hdr_beg': acl_hdr_beg,
        'hdr_end': acl_hdr_end,
        'hdr': acl_hdr,
        'hdr_reg': acl_hdr_beg,
        'hdr_sub': acl_hdr_sub,
        'path_beg': acl_path_beg,
        'path_end': acl_path_end,
        'path': acl_path,
        'path_reg': acl_path_reg,
        'path_dir': acl_path_dir,
        'path_sub': acl_path_sub,
        'url_param': acl_url_param,
        'url_param_value': acl_url_param_value,
        'ssl_c_verify_code': acl_ssl_c_verify_code,
        'ssl_c_ca_commonname': acl_ssl_c_ca_commonname,
        'src': acl_src,
        'src_bytes_in_rate_comparison': acl_src_bytes_in_rate_comparison,
        'src_bytes_in_rate': acl_src_bytes_in_rate,
        'src_bytes_out_rate_comparison': acl_src_bytes_out_rate_comparison,
        'src_bytes_out_rate': acl_src_bytes_out_rate,
        'src_conn_cnt_comparison': acl_src_conn_cnt_comparison,
        'src_conn_cnt': acl_src_conn_cnt,
        'src_conn_rate_comparison': acl_src_conn_rate_comparison,
        'src_conn_rate': acl_src_conn_rate,
        'src_http_err_cnt_comparison': acl_src_http_err_cnt_comparison,
        'src_http_err_cnt': acl_src_http_err_cnt,
        'src_http_err_rate_comparison': acl_src_http_err_rate_comparison,
        'src_http_err_rate': acl_src_http_err_rate,
        'src_http_req_rate_comparison':  acl_src_http_req_rate_comparison,
        'src_http_req_rate': acl_src_http_req_rate,
        'src_kbytes_in_comparison': acl_src_kbytes_in_comparison,
        'src_kbytes_in': acl_src_kbytes_in,
        'src_kbytes_out_comparison': acl_src_kbytes_out_comparison,
        'src_kbytes_out': acl_src_kbytes_out,
        'src_port_comparison': acl_src_port_comparison,
        'src_port': acl_src_port,
        'src_sess_cnt_comparison': acl_src_sess_cnt_comparison,
        'src_sess_cnt': acl_src_sess_cnt,
        'nbsrv': acl_nbsrv,
        'nbsrv_backend': acl_nbsrv_backend_uuid,
        'ssl_fc_sni': acl_ssl_fc_sni,
        'ssl_sni': acl_ssl_sni,
        'ssl_sni_sub': acl_ssl_sni_sub,
        'ssl_sni_beg': acl_ssl_sni_beg,
        'ssl_sni_end': acl_ssl_sni_end,
        'ssl_sni_reg': acl_ssl_sni_reg,
        'custom_acl': acl_custom_acl,
        'value': acl_value,
        'queryBackend': acl_query_backend,
        'allowedUsers': ','.join(acl_allowed_users_uuids),
        'allowedGroups': ','.join(acl_allowed_groups_uuids)
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if acl object with specified name exists
    for acl in acls:
        if acl['name'] == acl_name:
            uuid = acl['uuid']
            break
    acl_exists = (uuid != '')
    if acl_state == 'present':
        if acl_exists:
            acl = apiconnection.getObjectByName('acl', acl_name)
            for prop in desired_properties.keys():
                # Special cases for complex propertierts:
                if prop == 'expression':
                    current_expression = apiconnection.getSelected(acl[prop])
                    if current_expression != acl_expression:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_expression, desired_properties[prop]))
                # Catch all _comparison properties
                elif '_comparison' in prop:
                    current_comparison = apiconnection.getSelected(acl[prop])
                    if current_comparison != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_comparison, desired_properties[prop]))
                elif prop == 'nbsrv_backend':
                    current_nbsrv_backend = apiconnection.getSelected(acl[prop])
                    if current_nbsrv_backend != acl_nbsrv_backend:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_nbsrv_backend, desired_properties[prop]))
                elif prop == 'queryBackend':
                    current_query_backend = apiconnection.getSelected(acl[prop])
                    if current_query_backend != acl_query_backend:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_query_backend, desired_properties[prop]))
                elif prop == 'allowedUsers':
                    current_allowed_users = apiconnection.getSelectedList(acl[prop])
                    if not apiconnection.compareLists(current_allowed_users, acl_allowed_users_uuids):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_allowed_users, desired_properties[prop]))
                elif prop == 'allowedGroups':
                    current_allowed_groups = apiconnection.getSelectedList(acl[prop])
                    if not apiconnection.compareLists(current_allowed_groups, acl_allowed_groups_uuids):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, current_allowed_groups, desired_properties[prop]))
                else:
                    # catch all other properties
                    if acl[prop] != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %(prop, acl[prop], desired_properties[prop]))

            if not needs_change:
                result = {'changed': False, 'msg': ['Acl already present: %s' %acl_name, additional_msg]}
            else:
                if not module.check_mode:
                    additional_msg.append(apiconnection.updateObject('acl', acl_name, changed_properties))
                    if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
                result = {'changed': True, 'msg': ['Acl %s must be changed.' %acl_name, additional_msg]}
        else:
            if not module.check_mode:
                additional_msg.append(apiconnection.createObject('acl', acl_name, desired_properties))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['Acl %s must be created.' %acl_name, additional_msg]}
    else:
        if acl_exists:
            if not module.check_mode:
                additional_msg.append(apiconnection.deleteObject('acl', acl_name))
                if haproxy_reload: additional_msg.append(apiconnection.applyConfig())
            result = {'changed': True, 'msg': ['Acl %s must be deleted.' %acl_name, additional_msg]}
        else:
            result = {'changed': False, 'msg': ['Acl %s is not present.' %acl_name]}

    module.exit_json(**result)
Esempio n. 10
0
def main():

    global module
    # Instantiate module
    module = AnsibleModule(
        argument_spec=dict(
            api_url=dict(type='str', required=True),
            api_key=dict(type='str', required=True, no_log=True),
            api_secret=dict(type='str', required=True, no_log=True),
            api_ssl_verify=dict(type='bool', default=False),
            frontend_state=dict(type='str',
                                choices=['present', 'absent'],
                                default='present'),
            frontend_enabled=dict(type='bool', default=True),
            frontend_name=dict(type='str', required=True),
            frontend_description=dict(type='str', default=''),
            frontend_bind=dict(type='list', required=True),
            frontend_bind_options=dict(type='str', default=''),
            frontend_mode=dict(type='str',
                               choices=['http', 'ssl', 'tcp'],
                               default='http'),
            frontend_default_backend=dict(type='str', default='none'),
            frontend_ssl_enabled=dict(type='bool', default=False),
            frontend_ssl_certificates=dict(type='list', default=[]),
            frontend_ssl_default_certificate=dict(type='str', default=''),
            frontend_ssl_custom_options=dict(type='str', default=''),
            frontend_ssl_advanced_enabled=dict(type='bool', default=False),
            frontend_ssl_bind_options=dict(type='list',
                                           elements='str',
                                           options=dict(choices=[
                                               'no-sslv3', 'no-tlsv10',
                                               'no-tlsv11', 'no-tlsv12',
                                               'no-tls-tickets', 'force-sslv3',
                                               'force-tlsv10', 'force-tlsv11',
                                               'force-tlsv12', 'strict-sni'
                                           ], ),
                                           default=[]),
            frontend_ssl_cipher_list=dict(
                type='str',
                default=
                'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'
            ),
            frontend_ssl_http2_enabled=dict(type='bool', default=False),
            frontend_ssl_hsts_enabled=dict(type='bool', default=True),
            frontend_ssl_hsts_include_sub_domains=dict(type='bool',
                                                       default=False),
            frontend_ssl_hsts_preload=dict(type='bool', default=False),
            frontend_ssl_hsts_max_age=dict(type='str', default='15768000'),
            frontend_ssl_client_auth_enabled=dict(type='bool', default=False),
            frontend_ssl_client_auth_verify=dict(
                type='str',
                choices=['none', 'optional', 'required'],
                default='none'),
            frontend_ssl_client_auth_cas=dict(type='list', default=[]),
            frontend_ssl_client_auth_crls=dict(type='list', default=[]),
            frontend_basic_auth_enabled=dict(type='bool', default=False),
            frontend_basic_auth_users=dict(type='list', default=[]),
            frontend_basic_auth_groups=dict(type='list', default=[]),
            frontend_tuning_max_connections=dict(type='str', default=''),
            frontend_tuning_timeout_client=dict(type='str', default=''),
            frontend_tuning_timeout_http_req=dict(type='str', default=''),
            frontend_tuning_timeout_http_keep_alive=dict(type='str',
                                                         default=''),
            frontend_linked_cpu_affinity_rules=dict(type='list', default=[]),
            frontend_logging_dont_log_null=dict(type='bool', default=False),
            frontend_logging_dont_log_normal=dict(type='bool', default=False),
            frontend_logging_log_separate_errors=dict(type='bool',
                                                      default=False),
            frontend_logging_detailed_log=dict(type='bool', default=False),
            frontend_logging_socket_stats=dict(type='bool', default=False),
            frontend_stickiness_pattern=dict(
                type='str',
                choices=['ipv4', 'ipv6', 'integer', 'string', 'binary'],
                default='ipv4'),
            frontend_stickiness_data_types=dict(
                type='list',
                elements='str',
                options=dict(choices=[
                    'conn_cnt', 'conn_cur', 'conn_rate', 'sess_cnt',
                    'sess_rate', 'http_req_cnt', 'http_req_rate',
                    'http_err_cnt', 'http_err_rate', 'bytes_in_cnt',
                    'bytes_in_rate', 'bytes_out_cnt', 'bytes_out_rate'
                ], ),
                default=[]),
            frontend_stickiness_expire=dict(type='str', default='30m'),
            frontend_stickiness_size=dict(type='str', default='50k'),
            frontend_stickiness_counter=dict(type='bool', default=True),
            frontend_stickiness_counter_key=dict(type='str', default='src'),
            frontend_stickiness_length=dict(type='str', default=''),
            frontend_stickiness_conn_rate_period=dict(type='str',
                                                      default='10s'),
            frontend_stickiness_sess_rate_period=dict(type='str',
                                                      default='10s'),
            frontend_stickiness_http_req_rate_period=dict(type='str',
                                                          default='10s'),
            frontend_stickiness_http_err_rate_period=dict(type='str',
                                                          default='10s'),
            frontend_stickiness_bytes_in_rate_period=dict(type='str',
                                                          default='1m'),
            frontend_stickiness_bytes_out_rate_period=dict(type='str',
                                                           default='1m'),
            frontend_forward_for=dict(type='bool', default=False),
            frontend_connection_behaviour=dict(type='str',
                                               choices=[
                                                   'http-keep-alive',
                                                   'http-tunnel', 'httpclose',
                                                   'http-server-close',
                                                   'forceclose'
                                               ],
                                               default='http-keep-alive'),
            frontend_custom_options=dict(type='str', default=''),
            frontend_linked_actions=dict(type='list', default=[]),
            frontend_linked_errorfiles=dict(type='list', default=[]),
            haproxy_reload=dict(type='bool', default=False),
        ),
        supports_check_mode=True,
    )
    haproxy_reload = module.params['haproxy_reload']
    # Prepare properties of frontend
    frontend_state = module.params['frontend_state']
    frontend_enabled = str(int(module.params['frontend_enabled']))
    frontend_name = module.params['frontend_name']
    frontend_description = module.params['frontend_description']
    frontend_bind = module.params['frontend_bind']
    frontend_bind_options = module.params['frontend_bind_options']
    frontend_mode = module.params['frontend_mode']
    frontend_default_backend = module.params['frontend_default_backend']
    frontend_ssl_enabled = module.params['frontend_ssl_enabled']
    frontend_ssl_certificates = module.params['frontend_ssl_certificates']
    frontend_ssl_default_certificate = module.params[
        'frontend_ssl_default_certificate']
    frontend_ssl_custom_options = module.params['frontend_ssl_custom_options']
    frontend_ssl_advanced_enabled = module.params[
        'frontend_ssl_advanced_enabled']
    frontend_ssl_bind_options = module.params['frontend_ssl_bind_options']
    frontend_ssl_cipher_list = module.params['frontend_ssl_cipher_list']
    frontend_ssl_http2_enabled = module.params['frontend_ssl_http2_enabled']
    frontend_ssl_hsts_enabled = module.params['frontend_ssl_hsts_enabled']
    frontend_ssl_hsts_include_sub_domains = module.params[
        'frontend_ssl_hsts_include_sub_domains']
    frontend_ssl_hsts_preload = module.params['frontend_ssl_hsts_preload']
    frontend_ssl_hsts_max_age = module.params['frontend_ssl_hsts_max_age']
    frontend_ssl_client_auth_enabled = module.params[
        'frontend_ssl_client_auth_enabled']
    frontend_ssl_client_auth_verify = module.params[
        'frontend_ssl_client_auth_verify']
    frontend_ssl_client_auth_cas = module.params[
        'frontend_ssl_client_auth_cas']
    frontend_ssl_client_auth_crls = module.params[
        'frontend_ssl_client_auth_crls']
    frontend_basic_auth_enabled = module.params['frontend_basic_auth_enabled']
    frontend_basic_auth_users = module.params['frontend_basic_auth_users']
    frontend_basic_auth_groups = module.params['frontend_basic_auth_groups']
    frontend_tuning_max_connections = module.params[
        'frontend_tuning_max_connections']
    frontend_tuning_timeout_client = module.params[
        'frontend_tuning_timeout_client']
    frontend_tuning_timeout_http_req = module.params[
        'frontend_tuning_timeout_http_req']
    frontend_tuning_timeout_http_keep_alive = module.params[
        'frontend_tuning_timeout_http_keep_alive']
    frontend_linked_cpu_affinity_rules = module.params[
        'frontend_linked_cpu_affinity_rules']
    frontend_logging_dont_log_null = module.params[
        'frontend_logging_dont_log_null']
    frontend_logging_dont_log_normal = module.params[
        'frontend_logging_dont_log_normal']
    frontend_logging_log_separate_errors = module.params[
        'frontend_logging_log_separate_errors']
    frontend_logging_detailed_log = module.params[
        'frontend_logging_detailed_log']
    frontend_logging_socket_stats = module.params[
        'frontend_logging_socket_stats']
    frontend_stickiness_pattern = module.params['frontend_stickiness_pattern']
    frontend_stickiness_data_types = module.params[
        'frontend_stickiness_data_types']
    frontend_stickiness_expire = module.params['frontend_stickiness_expire']
    frontend_stickiness_size = module.params['frontend_stickiness_size']
    frontend_stickiness_counter = module.params['frontend_stickiness_counter']
    frontend_stickiness_counter_key = module.params[
        'frontend_stickiness_counter_key']
    frontend_stickiness_length = module.params['frontend_stickiness_length']
    frontend_stickiness_conn_rate_period = module.params[
        'frontend_stickiness_conn_rate_period']
    frontend_stickiness_sess_rate_period = module.params[
        'frontend_stickiness_sess_rate_period']
    frontend_stickiness_http_req_rate_period = module.params[
        'frontend_stickiness_http_req_rate_period']
    frontend_stickiness_http_err_rate_period = module.params[
        'frontend_stickiness_http_err_rate_period']
    frontend_stickiness_bytes_in_rate_period = module.params[
        'frontend_stickiness_bytes_in_rate_period']
    frontend_stickiness_bytes_out_rate_period = module.params[
        'frontend_stickiness_bytes_out_rate_period']
    frontend_forward_for = module.params['frontend_forward_for']
    frontend_connection_behaviour = module.params[
        'frontend_connection_behaviour']
    frontend_custom_options = module.params['frontend_custom_options']
    frontend_linked_actions = module.params['frontend_linked_actions']
    frontend_linked_errorfiles = module.params['frontend_linked_errorfiles']

    # Instantiate API connection
    api_url = module.params['api_url']
    api_auth = (module.params['api_key'], module.params['api_secret'])
    api_ssl_verify = module.params['api_ssl_verify']
    apiconnection = OpnsenseApi.Haproxy(api_url, api_auth, api_ssl_verify)

    # Fetch list of frontends
    frontends = apiconnection.listObjects('frontend')

    # Get an empty default object:
    # - to determine API version (not yet implemented)
    # - to retrieve UUIDs for:
    #   - SSL objects (ssl_certificates, ssl_default_certificate, ssl_clientAuthCAs, ssl_clientAuthCRLs)
    #   - defaultBackend
    #   - basicAuthUsers
    #   - basicAuthGroups
    #   - linkedCpuAffinityRules
    #   - linkedActions
    #   - linkedErrorfiles
    empty_frontend = apiconnection.getObjectByUuid('frontend', '')
    # Need to get UUIDs for:
    # defaultBackend, basicAuthUsers, basicAuthGroups, linkedCpuAffinityRules, linkedActions, linkedErrorfiles
    frontend_default_backend_uuid = ''
    if frontend_default_backend != 'none':
        #frontend_default_backend_uuid = apiconnection.getUuidByName('backend', frontend_default_backend)
        for key, value in empty_frontend['defaultBackend'].iteritems():
            if value['value'] == frontend_default_backend:
                frontend_default_backend_uuid = key
    #frontend_basic_auth_users_uuids = apiconnection.getUuidsFromNames('user', frontend_basic_auth_users)
    frontend_basic_auth_users_uuids = []
    for frontend_basic_auth_user in frontend_basic_auth_users:
        for key, value in empty_frontend['basicAuthUsers'].iteritems():
            if value['value'] == frontend_basic_auth_user:
                frontend_basic_auth_users_uuids.append(key)
    #frontend_basic_auth_groups_uuids = apiconnection.getUuidsFromNames('group', frontend_basic_auth_groups)
    frontend_basic_auth_groups_uuids = []
    for frontend_basic_auth_group in frontend_basic_auth_groups:
        for key, value in empty_frontend['basicAuthGroups'].iteritems():
            if value['value'] == frontend_basic_auth_group:
                frontend_basic_auth_groups_uuids.append(key)
    #frontend_linked_cpu_affinity_rules_uuids = apiconnection.getUuidsFromNames('cpu', frontend_linked_cpu_affinity_rules)
    frontend_linked_cpu_affinity_rules_uuids = []
    for frontend_linked_cpu_affinity_rule in frontend_linked_cpu_affinity_rules:
        for key, value in empty_frontend['linkedCpuAffinityRules'].iteritems():
            if value['value'] == frontend_linked_cpu_affinity_rule:
                frontend_linked_cpu_affinity_rules_uuids.append(key)
    #frontend_linked_actions_uuids = apiconnection.getUuidsFromNames('action', frontend_linked_actions)
    frontend_linked_actions_uuids = []
    for frontend_linked_action in frontend_linked_actions:
        for key, value in empty_frontend['linkedActions'].iteritems():
            if value['value'] == frontend_linked_action:
                frontend_linked_actions_uuids.append(key)
    #frontend_linked_errorfiles_uuids = apiconnection.getUuidsFromNames('errorfile', frontend_linked_errorfiles)
    frontend_linked_errorfiles_uuids = []
    for frontend_linked_errorfile in frontend_linked_errorfiles:
        for key, value in empty_frontend['linkedErrorfiles'].iteritems():
            if value['value'] == frontend_linked_errorfile:
                frontend_linked_errorfiles_uuids.append(key)
    frontend_ssl_certificates_uuids = apiconnection.getSslObjectKeys(
        empty_frontend['ssl_certificates'], frontend_ssl_certificates)
    # Only fetch default SSL cert uuid when one is set, otherwise supply an empty string
    frontend_ssl_default_certificate_uuid = ''
    if frontend_ssl_default_certificate != '':
        frontend_ssl_default_certificate_uuid = apiconnection.getSslObjectKeys(
            empty_frontend['ssl_default_certificate'],
            [frontend_ssl_default_certificate])[0]
    frontend_ssl_client_auth_cas_uuids = apiconnection.getSslObjectKeys(
        empty_frontend['ssl_clientAuthCAs'], frontend_ssl_client_auth_cas)
    frontend_ssl_client_auth_crls_uuids = apiconnection.getSslObjectKeys(
        empty_frontend['ssl_clientAuthCRLs'], frontend_ssl_client_auth_crls)
    # Build dict with desired state
    desired_properties = {
        'enabled':
        frontend_enabled,
        'description':
        frontend_description,
        'bind':
        ','.join(frontend_bind),
        'bindOptions':
        frontend_bind_options,
        'mode':
        frontend_mode,
        'defaultBackend':
        frontend_default_backend_uuid,
        'ssl_enabled':
        str(int(frontend_ssl_enabled)),
        'ssl_certificates':
        ','.join(frontend_ssl_certificates_uuids),
        'ssl_default_certificate':
        frontend_ssl_default_certificate_uuid,
        'ssl_customOptions':
        frontend_ssl_custom_options,
        'ssl_advancedEnabled':
        str(int(frontend_ssl_advanced_enabled)),
        'ssl_bindOptions':
        ','.join(frontend_ssl_bind_options),
        'ssl_cipherList':
        frontend_ssl_cipher_list,
        'ssl_http2Enabled':
        str(int(frontend_ssl_http2_enabled)),
        'ssl_hstsEnabled':
        str(int(frontend_ssl_hsts_enabled)),
        'ssl_hstsIncludeSubDomains':
        str(int(frontend_ssl_hsts_include_sub_domains)),
        'ssl_hstsPreload':
        str(int(frontend_ssl_hsts_preload)),
        'ssl_hstsMaxAge':
        frontend_ssl_hsts_max_age,
        'ssl_clientAuthEnabled':
        str(int(frontend_ssl_client_auth_enabled)),
        'ssl_clientAuthVerify':
        frontend_ssl_client_auth_verify,
        'ssl_clientAuthCAs':
        ','.join(frontend_ssl_client_auth_cas_uuids),
        'ssl_clientAuthCRLs':
        ','.join(frontend_ssl_client_auth_crls_uuids),
        'basicAuthEnabled':
        str(int(frontend_basic_auth_enabled)),
        'basicAuthUsers':
        ','.join(frontend_basic_auth_users_uuids),
        'basicAuthGroups':
        ','.join(frontend_basic_auth_groups_uuids),
        'tuning_maxConnections':
        frontend_tuning_max_connections,
        'tuning_timeoutClient':
        frontend_tuning_timeout_client,
        'tuning_timeoutHttpReq':
        frontend_tuning_timeout_http_req,
        'tuning_timeoutHttpKeepAlive':
        frontend_tuning_timeout_http_keep_alive,
        'linkedCpuAffinityRules':
        ','.join(frontend_linked_cpu_affinity_rules_uuids),
        'logging_dontLogNull':
        str(int(frontend_logging_dont_log_null)),
        'logging_dontLogNormal':
        str(int(frontend_logging_dont_log_normal)),
        'logging_logSeparateErrors':
        str(int(frontend_logging_log_separate_errors)),
        'logging_detailedLog':
        str(int(frontend_logging_detailed_log)),
        'logging_socketStats':
        str(int(frontend_logging_socket_stats)),
        'stickiness_pattern':
        frontend_stickiness_pattern,
        'stickiness_dataTypes':
        ','.join(frontend_stickiness_data_types),
        'stickiness_expire':
        frontend_stickiness_expire,
        'stickiness_size':
        frontend_stickiness_size,
        'stickiness_counter':
        str(int(frontend_stickiness_counter)),
        'stickiness_counter_key':
        frontend_stickiness_counter_key,
        'stickiness_length':
        frontend_stickiness_length,
        'stickiness_connRatePeriod':
        frontend_stickiness_conn_rate_period,
        'stickiness_sessRatePeriod':
        frontend_stickiness_sess_rate_period,
        'stickiness_httpReqRatePeriod':
        frontend_stickiness_http_req_rate_period,
        'stickiness_httpErrRatePeriod':
        frontend_stickiness_http_err_rate_period,
        'stickiness_bytesInRatePeriod':
        frontend_stickiness_bytes_in_rate_period,
        'stickiness_bytesOutRatePeriod':
        frontend_stickiness_bytes_out_rate_period,
        'forwardFor':
        str(int(frontend_forward_for)),
        'connectionBehaviour':
        frontend_connection_behaviour,
        'customOptions':
        frontend_custom_options,
        'linkedActions':
        ','.join(frontend_linked_actions_uuids),
        'linkedErrorfiles':
        ','.join(frontend_linked_errorfiles_uuids)
    }
    # Prepare dict with properties needing change
    changed_properties = {}
    # Prepare result dict
    result = {}
    additional_msg = []
    # Initialize some control vars
    needs_change = False
    uuid = ''
    # Check if frontend object with specified name exists
    for frontend in frontends:
        if frontend['name'] == frontend_name:
            uuid = frontend['uuid']
            break
    frontend_exists = (uuid != '')
    if frontend_state == 'present':
        if frontend_exists:
            frontend = apiconnection.getObjectByName('frontend', frontend_name)
            for prop in desired_properties.keys():
                # Special cases for complex propertierts:
                if prop == 'bind':
                    current_binds = apiconnection.getSelectedList(
                        frontend[prop])
                    if not apiconnection.compareLists(frontend_bind,
                                                      frontend[prop]):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Property %s must be changed' %
                                              prop)
                elif prop == 'mode':
                    current_mode = apiconnection.getSelected(frontend[prop])
                    if current_mode != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Property %s must be changed' %
                                              prop)
                elif prop == 'defaultBackend':
                    current_default_backend = apiconnection.getSelected(
                        frontend[prop], retval='value')
                    if current_default_backend != frontend_default_backend:
                        # Check if current default backend is an empty string and we don't want to have one:
                        if current_default_backend == '' and frontend_default_backend == 'none':
                            pass
                        else:
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_default_backend,
                                 desired_properties[prop]))
                elif prop == 'ssl_certificates' and frontend_ssl_enabled:
                    current_ssl_certificates = apiconnection.getSelectedList(
                        frontend[prop], retval='key')
                    if not apiconnection.compareLists(
                            current_ssl_certificates,
                            frontend_ssl_certificates_uuids):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Property %s must be changed' %
                                              prop)
                elif prop == 'ssl_default_certificate' and frontend_ssl_enabled:
                    current_ssl_default_certificate = apiconnection.getSelected(
                        frontend[prop])
                    if current_ssl_default_certificate != desired_properties[
                            prop]:
                        # Check if current default certificate is an empty string and we don't want to have one:
                        #if current_ssl_default_certificate == '' and frontend_ssl_default_certificate == 'none':
                        #    pass
                        #else:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_ssl_default_certificate,
                             desired_properties[prop]))
                elif prop == 'ssl_bindOptions' and frontend_ssl_enabled:
                    current_ssl_bind_options = apiconnection.getSelectedList(
                        frontend[prop])
                    if not apiconnection.compareLists(
                            current_ssl_bind_options,
                            frontend_ssl_bind_options):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_ssl_bind_options,
                                               desired_properties[prop]))
                # only care about ssl_clientAuthVerify when SSL client auth is enabled
                elif prop == 'ssl_clientAuthVerify' and frontend_ssl_enabled:
                    if not frontend_ssl_client_auth_enabled:
                        pass
                    else:
                        current_ssl_client_auth_verify = apiconnection.getSelected(
                            frontend[prop])
                        if current_ssl_client_auth_verify != desired_properties[
                                prop]:
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_ssl_client_auth_verify,
                                 desired_properties[prop]))
                # only care about ssl_clientAuthVerify when SSL client auth is enabled
                elif prop == 'ssl_clientAuthCAs' and frontend_ssl_enabled:
                    if not frontend_ssl_client_auth_enabled:
                        pass
                    else:
                        current_ssl_client_auth_cas = apiconnection.getSelectedList(
                            frontend[prop])
                        if not apiconnection.compareLists(
                                current_ssl_client_auth_cas,
                                frontend_ssl_client_auth_cas_uuids):
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_ssl_client_auth_cas,
                                 desired_properties[prop]))
                # only care about ssl_clientAuthVerify when SSL client auth is enabled
                elif prop == 'ssl_clientAuthCRLs' and frontend_ssl_enabled:
                    if not frontend_ssl_client_auth_enabled:
                        pass
                    else:
                        current_ssl_client_auth_crls = apiconnection.getSelectedList(
                            frontend[prop])
                        if not apiconnection.compareLists(
                                current_ssl_client_auth_crls,
                                frontend_ssl_client_auth_crls_uuids):
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_ssl_client_auth_crls,
                                 desired_properties[prop]))
                elif prop == 'basicAuthUsers':
                    # only care about basicAuthUsers when basic auth is enabled
                    if frontend_basic_auth_enabled:
                        current_basic_auth_users = apiconnection.getSelectedList(
                            frontend[prop])
                        if not apiconnection.compareLists(
                                current_basic_auth_users,
                                frontend_basic_auth_users):
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_basic_auth_users,
                                 desired_properties[prop]))
                elif prop == 'basicAuthGroups':
                    # only care about basicAuthGroups when basic auth is enabled
                    if frontend_basic_auth_enabled:
                        current_basic_auth_groups = apiconnection.getSelectedList(
                            frontend[prop])
                        if not apiconnection.compareLists(
                                current_basic_auth_groups,
                                frontend_basic_auth_groups):
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append(
                                'Changing %s: %s => %s' %
                                (prop, current_basic_auth_groups,
                                 desired_properties[prop]))
                elif prop == 'stickiness_pattern':
                    current_stickiness_pattern = apiconnection.getSelected(
                        frontend[prop])
                    if current_stickiness_pattern != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_stickiness_pattern,
                             desired_properties[prop]))
                elif prop == 'stickiness_dataTypes':
                    current_stickiness_data_types = apiconnection.getSelected(
                        frontend[prop])
                    if not apiconnection.compareLists(
                            current_stickiness_data_types,
                            frontend_stickiness_data_types):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_stickiness_data_types,
                             desired_properties[prop]))
                    pass
                elif prop == 'connectionBehaviour':
                    current_connection_behaviour = apiconnection.getSelected(
                        frontend[prop])
                    if current_connection_behaviour != desired_properties[prop]:
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_connection_behaviour,
                             desired_properties[prop]))
                elif prop == 'linkedCpuAffinityRules':
                    current_linked_cpu_affinity_rules = apiconnection.getSelectedList(
                        frontend[prop], retval='key')
                    if not apiconnection.compareLists(
                            current_linked_cpu_affinity_rules,
                            frontend_linked_cpu_affinity_rules):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append(
                            'Changing %s: %s => %s' %
                            (prop, current_linked_cpu_affinity_rules,
                             desired_properties[prop]))
                elif prop == 'linkedActions':
                    current_linked_actions = apiconnection.getSelectedList(
                        frontend[prop], retval='key')
                    if not apiconnection.compareLists(
                            current_linked_actions,
                            frontend_linked_actions_uuids,
                            order_sensitive=True):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_linked_actions,
                                               desired_properties[prop]))
                elif prop == 'linkedErrorfiles':
                    current_linked_errorfiles = apiconnection.getSelectedList(
                        frontend[prop], retval='key')
                    if not apiconnection.compareLists(
                            current_linked_errorfiles,
                            frontend_linked_errorfiles_uuids):
                        needs_change = True
                        changed_properties[prop] = desired_properties[prop]
                        additional_msg.append('Changing %s: %s => %s' %
                                              (prop, current_linked_errorfiles,
                                               desired_properties[prop]))
                else:
                    # only care about SSL properties when it should be enabled
                    if 'ssl' in prop and not frontend_ssl_enabled:
                        # SSL enabled must still be evaluated
                        if prop == 'ssl_enabled' and frontend[
                                prop] != desired_properties[prop]:
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append('Changing %s: %s => %s' %
                                                  (prop, frontend[prop],
                                                   desired_properties[prop]))
                        else:
                            pass
                    elif 'ssl' in prop and frontend_ssl_enabled:
                        # Some properties are only stored when enabling advanced SSL options
                        if not frontend_ssl_advanced_enabled and prop == 'ssl_bindOptions':
                            pass
                        elif not frontend_ssl_advanced_enabled and prop == 'ssl_cipherList':
                            pass
                        elif not frontend_ssl_advanced_enabled and prop == 'ssl_http2Enabled':
                            pass
                        elif not frontend_ssl_advanced_enabled and prop == 'ssl_hstsEnabled':
                            pass
                        elif not frontend_ssl_advanced_enabled and prop == 'ssl_hstsIncludeSubdomains':
                            pass
                        elif not frontend_ssl_advanced_enabled and prop == 'ssl_hstsPreload':
                            pass
                        elif not frontend_ssl_advanced_enabled and prop == 'ssl_hstsMaxAge':
                            pass
                        else:
                            if prop not in frontend:
                                # prop might not be present in current state
                                needs_change = True
                                changed_properties[prop] = desired_properties[
                                    prop]
                                additional_msg.append(
                                    'Changing %s: %s => %s' %
                                    (prop, '', desired_properties[prop]))
                            else:
                                if frontend[prop] != desired_properties[prop]:
                                    needs_change = True
                                    changed_properties[
                                        prop] = desired_properties[prop]
                                    additional_msg.append(
                                        'Changing %s: %s => %s' %
                                        (prop, frontend[prop],
                                         desired_properties[prop]))
                    # catch all other properties
                    else:
                        if frontend[prop] != desired_properties[prop]:
                            needs_change = True
                            changed_properties[prop] = desired_properties[prop]
                            additional_msg.append('Changing %s: %s => %s' %
                                                  (prop, frontend[prop],
                                                   desired_properties[prop]))

            if not needs_change:
                result = {
                    'changed':
                    False,
                    'msg': [
                        'Frontend already present: %s' % frontend_name,
                        additional_msg
                    ]
                }
            else:
                if not module.check_mode:
                    # workaround for https://github.com/opnsense/plugins/issues/1494
                    # any change must include the linkedActions to maintain the correct order
                    changed_properties['linkedActions'] = desired_properties[
                        'linkedActions']
                    additional_msg.append(
                        apiconnection.updateObject('frontend', frontend_name,
                                                   changed_properties))
                    if haproxy_reload:
                        additional_msg.append(apiconnection.applyConfig())
                result = {
                    'changed':
                    True,
                    'msg': [
                        'Frontend %s must be changed.' % frontend_name,
                        additional_msg
                    ]
                }
        else:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.createObject('frontend', frontend_name,
                                               desired_properties))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg': [
                    'Frontend %s must be created.' % frontend_name,
                    additional_msg
                ]
            }
    else:
        if frontend_exists:
            if not module.check_mode:
                additional_msg.append(
                    apiconnection.deleteObject('frontend', frontend_name))
                if haproxy_reload:
                    additional_msg.append(apiconnection.applyConfig())
            result = {
                'changed':
                True,
                'msg': [
                    'Frontend %s must be deleted.' % frontend_name,
                    additional_msg
                ]
            }
        else:
            result = {
                'changed': False,
                'msg': ['Frontend %s is not present.' % frontend_name]
            }

    module.exit_json(**result)