Ensures that an address object exists in the configured state. If it does not exist or is not configured with the
    specified attributes, it will be adjusted to match the specified values.

    This module will only process a single address type (ip-netmask, ip-range, or fqdn). It will process the specified
    value if the following order: ip-netmask, ip-range, fqdn. For proper execution, only specify a single address
    type.

    name: The name of the module function to execute.

    addressname(str): The name of the address object.  The name is case-sensitive and can have up to 31 characters,
    which an be letters, numbers, spaces, hyphens, and underscores. The name must be unique on a firewall and, on
    Panorama, unique within its device group and any ancestor or descendant device groups.

    vsys(str): The string representation of the VSYS ID. Defaults to VSYS 1.

    ipnetmask(str): The IPv4 or IPv6 address or IP address range using the format ip_address/mask or ip_address where
    the mask is the number of significant binary digits used for the network portion of the address. Ideally, for IPv6,
    you specify only the network portion, not the host portion.

    iprange(str): A range of addresses using the format ip_address–ip_address where both addresses can be  IPv4 or both
    can be IPv6.

    fqdn(str): A fully qualified domain name format. The FQDN initially resolves at commit time. Entries are
    subsequently refreshed when the firewall performs a check every 30 minutes; all changes in the IP address for the
    entries are picked up at the refresh cycle.

    description(str): A description for the policy (up to 255 characters).

    commit(bool): If true the firewall will commit the changes, if false do not commit changes.

    SLS Example:

    .. code-block:: yaml

        panos/address/h-10.10.10.10:
            panos.address_exists:
              - addressname: h-10.10.10.10
              - vsys: 1
              - ipnetmask: 10.10.10.10
              - commit: False

        panos/address/10.0.0.1-10.0.0.50:
            panos.address_exists:
              - addressname: r-10.0.0.1-10.0.0.50
              - vsys: 1
              - iprange: 10.0.0.1-10.0.0.50
              - commit: False

        panos/address/foo.bar.com:
            panos.address_exists:
              - addressname: foo.bar.com
              - vsys: 1
              - fqdn: foo.bar.com
              - description: My fqdn object
              - commit: False

def address_exists(name,
                   addressname=None,
                   vsys=1,
                   ipnetmask=None,
                   iprange=None,
                   fqdn=None,
                   description=None,
                   commit=False):
    '''
    Ensures that an address object exists in the configured state. If it does not exist or is not configured with the
    specified attributes, it will be adjusted to match the specified values.

    This module will only process a single address type (ip-netmask, ip-range, or fqdn). It will process the specified
    value if the following order: ip-netmask, ip-range, fqdn. For proper execution, only specify a single address
    type.

    name: The name of the module function to execute.

    addressname(str): The name of the address object.  The name is case-sensitive and can have up to 31 characters,
    which an be letters, numbers, spaces, hyphens, and underscores. The name must be unique on a firewall and, on
    Panorama, unique within its device group and any ancestor or descendant device groups.

    vsys(str): The string representation of the VSYS ID. Defaults to VSYS 1.

    ipnetmask(str): The IPv4 or IPv6 address or IP address range using the format ip_address/mask or ip_address where
    the mask is the number of significant binary digits used for the network portion of the address. Ideally, for IPv6,
    you specify only the network portion, not the host portion.

    iprange(str): A range of addresses using the format ip_address–ip_address where both addresses can be  IPv4 or both
    can be IPv6.

    fqdn(str): A fully qualified domain name format. The FQDN initially resolves at commit time. Entries are
    subsequently refreshed when the firewall performs a check every 30 minutes; all changes in the IP address for the
    entries are picked up at the refresh cycle.

    description(str): A description for the policy (up to 255 characters).

    commit(bool): If true the firewall will commit the changes, if false do not commit changes.

    SLS Example:

    .. code-block:: yaml

        panos/address/h-10.10.10.10:
            panos.address_exists:
              - addressname: h-10.10.10.10
              - vsys: 1
              - ipnetmask: 10.10.10.10
              - commit: False

        panos/address/10.0.0.1-10.0.0.50:
            panos.address_exists:
              - addressname: r-10.0.0.1-10.0.0.50
              - vsys: 1
              - iprange: 10.0.0.1-10.0.0.50
              - commit: False

        panos/address/foo.bar.com:
            panos.address_exists:
              - addressname: foo.bar.com
              - vsys: 1
              - fqdn: foo.bar.com
              - description: My fqdn object
              - commit: False

    '''
    ret = _default_ret(name)

    if not addressname:
        ret.update({'comment': "The service name field must be provided."})
        return ret

    # Check if address object currently exists
    address = __salt__['panos.get_address'](addressname, vsys)['result']

    if address and 'entry' in address:
        address = address['entry']
    else:
        address = {}

    element = ""

    # Verify the arguments
    if ipnetmask:
        element = "<ip-netmask>{0}</ip-netmask>".format(ipnetmask)
    elif iprange:
        element = "<ip-range>{0}</ip-range>".format(iprange)
    elif fqdn:
        element = "<fqdn>{0}</fqdn>".format(fqdn)
    else:
        ret.update({'comment': "A valid address type must be specified."})
        return ret

    if description:
        element += "<description>{0}</description>".format(description)

    full_element = "<entry name='{0}'>{1}</entry>".format(addressname, element)

    new_address = xml.to_dict(ET.fromstring(full_element), True)

    if address == new_address:
        ret.update({
            'comment': 'Address object already exists. No changes required.',
            'result': True
        })
        return ret
    else:
        xpath = "/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/address/" \
                "entry[@name=\'{1}\']".format(vsys, addressname)

        result, msg = _edit_config(xpath, full_element)

        if not result:
            ret.update({
                'comment': msg
            })
            return ret

    if commit is True:
        ret.update({
            'changes': {'before': address, 'after': new_address},
            'commit': __salt__['panos.commit'](),
            'comment': 'Address object successfully configured.',
            'result': True
        })
    else:
        ret.update({
            'changes': {'before': address, 'after': new_address},
            'comment': 'Service object successfully configured.',
            'result': True
        })

    return ret