fmgr_provisioning

Metadata

Name: fmgr_provisioning

Description: Add model devices on the FortiManager using jsonrpc API and have them pre-configured, so when central management is configured, the configuration is pushed down to the registering devices

Author(s): Andrew Welsh (@Ghilli3)

Ansible Version Added/Required: 2.8

Dev Status: CODE UPDATE IN PROGRESS

Owning Developer: Andrew Welsh

Pull Request Started:

Days in PR:

Module Github Link

Parameters

adom

  • Description: The administrative domain (admon) the configuration belongs to.
  • default: root

description

  • Description: Description of the device to be provisioned.
  • Required: False

group

  • Description: The name of the device group the provisioned device can belong to.
  • Required: False

minor_release

  • Description: The minor release number such as 6.X.1, as X being the minor release.
  • Required: False

name

  • Description: The name of the device to be provisioned.
  • Required: True

os_type

  • Description: The Fortinet OS type to be pushed to the device, such as ‘FOS’ for FortiOS.
  • default: fos

os_version

  • Description: The Fortinet OS version to be used for the device, such as 5.0 or 6.0.
  • Required: True

patch_release

  • Description: The patch release number such as 6.0.X, as X being the patch release.
  • Required: False

platform

  • Description: The platform of the device, such as model number or VM.
  • default: FortiGate-VM64

policy_package

  • Description: The name of the policy package to be assigned to the device.
  • default: default

serial

  • Description: The serial number of the device that will be provisioned.
  • Required: True

vdom

  • Description: The virtual domain (vdom) the configuration belongs to.
  • default: root

Functions

  • dev_group_exists
def dev_group_exists(fmgr, paramgram):
    datagram = {
        'adom': paramgram["adom"],
        'name': paramgram["dev_grp_name"],
    }

    url = '/dvmdb/adom/{adom}/group/{dev_grp_name}'.format(adom=paramgram["adom"],
                                                           dev_grp_name=paramgram["dev_grp_name"])
    response = fmgr.process_request(url, datagram, FMGRMethods.GET)
    return response
  • prov_template_exists
def prov_template_exists(fmgr, paramgram):
    datagram = {
        'name': paramgram["prov_template"],
        'adom': paramgram["adom"],
    }

    url = '/pm/devprof/adom/{adom}/devprof/{name}'.format(adom=paramgram["adom"], name=paramgram["prov_template"])
    response = fmgr.process_request(url, datagram, FMGRMethods.GET)
    return response
  • create_model_device
def create_model_device(fmgr, paramgram):
    datagram = {
        'adom': paramgram["adom"],
        'flags': ['create_task', 'nonblocking'],
        'groups': [{'name': paramgram["group"], 'vdom': paramgram['vdom']}],
        'device': {
            'mr': paramgram["minor_release"],
            'name': paramgram["name"],
            'sn': paramgram["serial"],
            'mgmt_mode': 'fmg',
            'device action': 'add_model',
            'platform_str': paramgram["platform"],
            'os_ver': paramgram["os_version"],
            'os_type': paramgram["os_type"],
            'patch': paramgram["patch_release"],
            'desc': 'Provisioned by Ansible',
        }
    }

    url = '/dvm/cmd/add/device'
    response = fmgr.process_request(url, datagram, FMGRMethods.EXEC)
    return response
  • update_flags
def update_flags(fmgr, paramgram):
    datagram = {
        'flags': ['is_model', 'linked_to_model']
    }

    url = 'dvmdb/device/{name}'.format(name=paramgram["name"])
    response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
    return response
  • assign_provision_template
def assign_provision_template(fmgr, paramgram):
    datagram = {
        'name': paramgram["template"],
        'type': 'devprof',
        'description': 'Provisioned by Ansible',
        'scope member': [{'name': paramgram["target"]}]
    }

    url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"])
    response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
    return response
#
#
  • set_devprof_scope
# def set_devprof_scope(self, provisioning_template, adom, provision_targets):
#     """
#     :param fmgr: The fmgr object instance from fortimanager.py
#     :type fmgr: class object
#     :param paramgram: The formatted dictionary of options to process
#     :type paramgram: dict
#     :return: The response from the FortiManager
#     :rtype: dict
#     """
#     fields = dict()
#     targets = []
#     fields["name"] = provisioning_template
#     fields["type"] = "devprof"
#     fields["description"] = "CreatedByAnsible"
#
#     for target in provision_targets.strip().split(","):
#         # split the host on the space to get the mask out
#         new_target = {"name": target}
#         targets.append(new_target)
#
#     fields["scope member"] = targets
#     url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"])
#     body = {"method": "set", "params": [{"url": "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"]),
#                                          "data": fields, "session": self.session}]}
#     response = fmgr.process_request(url, body, FMGRMethods.SET)
#     return response
  • assign_dev_grp
def assign_dev_grp(fmgr, paramgram):
    datagram = {
        'name': paramgram["device_name"],
        'vdom': paramgram["vdom"],
    }

    url = "/dvmdb/adom/{adom}/group/{grp_name}/object member".format(adom=paramgram["adom"],
                                                                     grp_name=paramgram["grp_name"])
    response = fmgr.process_request(url, datagram, FMGRMethods.SET)
    return response
  • update_install_target
def update_install_target(fmgr, paramgram):
    datagram = {
        'scope member': [{'name': paramgram["device"], 'vdom': paramgram["vdom"]}],
        'type': 'pkg'
    }

    url = '/pm/pkg/adom/{adom}/{pkg_name}'.format(adom=paramgram["adom"], pkg_name=paramgram["policy_package"])
    response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
    return response
  • install_pp
def install_pp(fmgr, paramgram):
    datagram = {
        'adom': paramgram["adom"],
        'flags': 'nonblocking',
        'pkg': paramgram["policy_package"],
        'scope': [{'name': paramgram["device"], 'vdom': paramgram["vdom"]}],
    }

    url = 'securityconsole/install/package'
    response = fmgr.process_request(url, datagram, FMGRMethods.EXEC)
    return response
  • main
def main():

    argument_spec = dict(
        adom=dict(required=False, type="str", default="root"),
        vdom=dict(required=False, type="str", default="root"),
        policy_package=dict(required=False, type="str", default="default"),
        name=dict(required=True, type="str"),
        group=dict(required=False, type="str"),
        serial=dict(required=True, type="str"),
        platform=dict(required=False, type="str", default="FortiGate-VM64"),
        description=dict(required=False, type="str"),
        os_version=dict(required=True, type="str"),
        minor_release=dict(required=False, type="str"),
        patch_release=dict(required=False, type="str"),
        os_type=dict(required=False, type="str", default="fos"),

    )

    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, )

    paramgram = {
        "adom": module.params["adom"],
        "vdom": module.params["vdom"],
        "policy_package": module.params["policy_package"],
        "name": module.params["name"],
        "group": module.params["group"],
        "serial": module.params["serial"],
        "platform": module.params["platform"],
        "description": module.params["description"],
        "os_version": module.params["os_version"],
        "minor_release": module.params["minor_release"],
        "patch_release": module.params["patch_release"],
        "os_type": module.params["os_type"],
    }

    module.paramgram = paramgram
    fmgr = None
    if module._socket_path:
        connection = Connection(module._socket_path)
        fmgr = FortiManagerHandler(connection, module)
        fmgr.tools = FMGRCommon()
    else:
        module.fail_json(**FAIL_SOCKET_MSG)

    results = DEFAULT_RESULT_OBJ

    try:
        results = create_model_device(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Create model failed", **results)

        results = update_flags(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Update device flags failed", **results)

        results = update_install_target(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Adding device target to package failed", **results)

        results = install_pp(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Installing policy package failed", **results)

    except Exception as err:
        raise FMGBaseException(err)

    return module.exit_json(**results[1])

Module Source Code

#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
#

from __future__ import absolute_import, division, print_function

__metaclass__ = type

ANSIBLE_METADATA = {'status': ['preview'],
                    'supported_by': 'community',
                    'metadata_version': '1.1'}

DOCUMENTATION = '''
---
module: fmgr_provisioning
version_added: "2.8"
notes:
    - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/).
author: Andrew Welsh (@Ghilli3)
short_description: Provision devices via FortiMananger
description:
  - Add model devices on the FortiManager using jsonrpc API and have them pre-configured,
    so when central management is configured, the configuration is pushed down to the
    registering devices

options:
  adom:
    description:
      - The administrative domain (admon) the configuration belongs to.
    default: "root"

  vdom:
    description:
      - The virtual domain (vdom) the configuration belongs to.
    default: "root"

  policy_package:
    description:
      - The name of the policy package to be assigned to the device.
    default: "default"

  name:
    description:
      - The name of the device to be provisioned.
    required: True

  group:
    description:
      - The name of the device group the provisioned device can belong to.
    required: False

  serial:
    description:
      - The serial number of the device that will be provisioned.
    required: True

  platform:
    description:
      - The platform of the device, such as model number or VM.
    default: "FortiGate-VM64"

  description:
    description:
      - Description of the device to be provisioned.
    required: False

  os_version:
    description:
      - The Fortinet OS version to be used for the device, such as 5.0 or 6.0.
    required: True

  minor_release:
    description:
      - The minor release number such as 6.X.1, as X being the minor release.
    required: False

  patch_release:
    description:
      - The patch release number such as 6.0.X, as X being the patch release.
    required: False

  os_type:
    description:
      - The Fortinet OS type to be pushed to the device, such as 'FOS' for FortiOS.
    default: "fos"
'''

EXAMPLES = '''
- name: Create FGT1 Model Device
  fmgr_provisioning:
    adom: "root"
    vdom: "root"
    policy_package: "default"
    name: "FGT1"
    group: "Ansible"
    serial: "FGVM000000117994"
    platform: "FortiGate-VM64"
    description: "Provisioned by Ansible"
    os_version: '6.0'
    minor_release: 0
    patch_release: 0
    os_type: 'fos'


- name: Create FGT2 Model Device
  fmgr_provisioning:
    adom: "root"
    vdom: "root"
    policy_package: "test_pp"
    name: "FGT2"
    group: "Ansible"
    serial: "FGVM000000117992"
    platform: "FortiGate-VM64"
    description: "Provisioned by Ansible"
    os_version: '5.0'
    minor_release: 6
    patch_release: 0
    os_type: 'fos'

'''

RETURN = """
api_result:
  description: full API response, includes status code and message
  returned: always
  type: str
"""

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
from ansible.module_utils.network.fortimanager.fortimanager import FortiManagerHandler
from ansible.module_utils.network.fortimanager.common import FMGBaseException
from ansible.module_utils.network.fortimanager.common import FMGRCommon
from ansible.module_utils.network.fortimanager.common import DEFAULT_RESULT_OBJ
from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
from ansible.module_utils.network.fortimanager.common import FMGRMethods


def dev_group_exists(fmgr, paramgram):
    datagram = {
        'adom': paramgram["adom"],
        'name': paramgram["dev_grp_name"],
    }

    url = '/dvmdb/adom/{adom}/group/{dev_grp_name}'.format(adom=paramgram["adom"],
                                                           dev_grp_name=paramgram["dev_grp_name"])
    response = fmgr.process_request(url, datagram, FMGRMethods.GET)
    return response


def prov_template_exists(fmgr, paramgram):
    datagram = {
        'name': paramgram["prov_template"],
        'adom': paramgram["adom"],
    }

    url = '/pm/devprof/adom/{adom}/devprof/{name}'.format(adom=paramgram["adom"], name=paramgram["prov_template"])
    response = fmgr.process_request(url, datagram, FMGRMethods.GET)
    return response


def create_model_device(fmgr, paramgram):
    datagram = {
        'adom': paramgram["adom"],
        'flags': ['create_task', 'nonblocking'],
        'groups': [{'name': paramgram["group"], 'vdom': paramgram['vdom']}],
        'device': {
            'mr': paramgram["minor_release"],
            'name': paramgram["name"],
            'sn': paramgram["serial"],
            'mgmt_mode': 'fmg',
            'device action': 'add_model',
            'platform_str': paramgram["platform"],
            'os_ver': paramgram["os_version"],
            'os_type': paramgram["os_type"],
            'patch': paramgram["patch_release"],
            'desc': 'Provisioned by Ansible',
        }
    }

    url = '/dvm/cmd/add/device'
    response = fmgr.process_request(url, datagram, FMGRMethods.EXEC)
    return response


def update_flags(fmgr, paramgram):
    datagram = {
        'flags': ['is_model', 'linked_to_model']
    }

    url = 'dvmdb/device/{name}'.format(name=paramgram["name"])
    response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
    return response


def assign_provision_template(fmgr, paramgram):
    datagram = {
        'name': paramgram["template"],
        'type': 'devprof',
        'description': 'Provisioned by Ansible',
        'scope member': [{'name': paramgram["target"]}]
    }

    url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"])
    response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
    return response
#
#
# def set_devprof_scope(self, provisioning_template, adom, provision_targets):
#     """
#     :param fmgr: The fmgr object instance from fortimanager.py
#     :type fmgr: class object
#     :param paramgram: The formatted dictionary of options to process
#     :type paramgram: dict
#     :return: The response from the FortiManager
#     :rtype: dict
#     """
#     fields = dict()
#     targets = []
#     fields["name"] = provisioning_template
#     fields["type"] = "devprof"
#     fields["description"] = "CreatedByAnsible"
#
#     for target in provision_targets.strip().split(","):
#         # split the host on the space to get the mask out
#         new_target = {"name": target}
#         targets.append(new_target)
#
#     fields["scope member"] = targets
#     url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"])
#     body = {"method": "set", "params": [{"url": "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"]),
#                                          "data": fields, "session": self.session}]}
#     response = fmgr.process_request(url, body, FMGRMethods.SET)
#     return response


def assign_dev_grp(fmgr, paramgram):
    datagram = {
        'name': paramgram["device_name"],
        'vdom': paramgram["vdom"],
    }

    url = "/dvmdb/adom/{adom}/group/{grp_name}/object member".format(adom=paramgram["adom"],
                                                                     grp_name=paramgram["grp_name"])
    response = fmgr.process_request(url, datagram, FMGRMethods.SET)
    return response


def update_install_target(fmgr, paramgram):
    datagram = {
        'scope member': [{'name': paramgram["device"], 'vdom': paramgram["vdom"]}],
        'type': 'pkg'
    }

    url = '/pm/pkg/adom/{adom}/{pkg_name}'.format(adom=paramgram["adom"], pkg_name=paramgram["policy_package"])
    response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
    return response


def install_pp(fmgr, paramgram):
    datagram = {
        'adom': paramgram["adom"],
        'flags': 'nonblocking',
        'pkg': paramgram["policy_package"],
        'scope': [{'name': paramgram["device"], 'vdom': paramgram["vdom"]}],
    }

    url = 'securityconsole/install/package'
    response = fmgr.process_request(url, datagram, FMGRMethods.EXEC)
    return response


def main():

    argument_spec = dict(
        adom=dict(required=False, type="str", default="root"),
        vdom=dict(required=False, type="str", default="root"),
        policy_package=dict(required=False, type="str", default="default"),
        name=dict(required=True, type="str"),
        group=dict(required=False, type="str"),
        serial=dict(required=True, type="str"),
        platform=dict(required=False, type="str", default="FortiGate-VM64"),
        description=dict(required=False, type="str"),
        os_version=dict(required=True, type="str"),
        minor_release=dict(required=False, type="str"),
        patch_release=dict(required=False, type="str"),
        os_type=dict(required=False, type="str", default="fos"),

    )

    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, )

    paramgram = {
        "adom": module.params["adom"],
        "vdom": module.params["vdom"],
        "policy_package": module.params["policy_package"],
        "name": module.params["name"],
        "group": module.params["group"],
        "serial": module.params["serial"],
        "platform": module.params["platform"],
        "description": module.params["description"],
        "os_version": module.params["os_version"],
        "minor_release": module.params["minor_release"],
        "patch_release": module.params["patch_release"],
        "os_type": module.params["os_type"],
    }

    module.paramgram = paramgram
    fmgr = None
    if module._socket_path:
        connection = Connection(module._socket_path)
        fmgr = FortiManagerHandler(connection, module)
        fmgr.tools = FMGRCommon()
    else:
        module.fail_json(**FAIL_SOCKET_MSG)

    results = DEFAULT_RESULT_OBJ

    try:
        results = create_model_device(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Create model failed", **results)

        results = update_flags(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Update device flags failed", **results)

        results = update_install_target(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Adding device target to package failed", **results)

        results = install_pp(fmgr, paramgram)
        if results[0] != 0:
            module.fail_json(msg="Installing policy package failed", **results)

    except Exception as err:
        raise FMGBaseException(err)

    return module.exit_json(**results[1])


if __name__ == "__main__":
    main()