Ensures that a service 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.

    name: The name of the module function to execute.

    servicename(str): The name of the security 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.

    protocol(str): The protocol that is used by the service object. The only valid options are tcp and udp.

    port(str): The port number that is used by the service object. This can be specified as a single integer or a
    valid range of ports.

    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/service/tcp-80:
            panos.service_exists:
              - servicename: tcp-80
              - vsys: 1
              - protocol: tcp
              - port: 80
              - description: Hypertext Transfer Protocol
              - commit: False

        panos/service/udp-500-550:
            panos.service_exists:
              - servicename: udp-500-550
              - vsys: 3
              - protocol: udp
              - port: 500-550
              - commit: False

def service_exists(name, servicename=None, vsys=1, protocol=None, port=None, description=None, commit=False):
    '''
    Ensures that a service 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.

    name: The name of the module function to execute.

    servicename(str): The name of the security 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.

    protocol(str): The protocol that is used by the service object. The only valid options are tcp and udp.

    port(str): The port number that is used by the service object. This can be specified as a single integer or a
    valid range of ports.

    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/service/tcp-80:
            panos.service_exists:
              - servicename: tcp-80
              - vsys: 1
              - protocol: tcp
              - port: 80
              - description: Hypertext Transfer Protocol
              - commit: False

        panos/service/udp-500-550:
            panos.service_exists:
              - servicename: udp-500-550
              - vsys: 3
              - protocol: udp
              - port: 500-550
              - commit: False

    '''
    ret = _default_ret(name)

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

    # Check if service object currently exists
    service = __salt__['panos.get_service'](servicename, vsys)['result']

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

    # Verify the arguments
    if not protocol and protocol not in ['tcp', 'udp']:
        ret.update({'comment': "The protocol must be provided and must be tcp or udp."})
        return ret
    if not port:
        ret.update({'comment': "The port field must be provided."})
        return ret

    element = "<protocol><{0}><port>{1}</port></{0}></protocol>".format(protocol, port)

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

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

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

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

        result, msg = _edit_config(xpath, full_element)

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

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

    return ret