Ensures that a service group 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 enforce group membership. If a group exists and contains members this state does not include,
    those members will be removed and replaced with the specified members in the state.

    name: The name of the module function to execute.

    groupname(str): The name of the service group 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.

    members(str, list): The members of the service group. These must be valid service objects or service groups on the
    system that already exist prior to the execution of this state.

    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-group/my-group:
            panos.service_group_exists:
              - groupname: my-group
              - vsys: 1
              - members:
                - tcp-80
                - custom-port-group
              - description: A group that needs to exist
              - commit: False

def service_group_exists(name,
                         groupname=None,
                         vsys=1,
                         members=None,
                         description=None,
                         commit=False):
    '''
    Ensures that a service group 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 enforce group membership. If a group exists and contains members this state does not include,
    those members will be removed and replaced with the specified members in the state.

    name: The name of the module function to execute.

    groupname(str): The name of the service group 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.

    members(str, list): The members of the service group. These must be valid service objects or service groups on the
    system that already exist prior to the execution of this state.

    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-group/my-group:
            panos.service_group_exists:
              - groupname: my-group
              - vsys: 1
              - members:
                - tcp-80
                - custom-port-group
              - description: A group that needs to exist
              - commit: False

    '''
    ret = _default_ret(name)

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

    # Check if service group object currently exists
    group = __salt__['panos.get_service_group'](groupname, vsys)['result']

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

    # Verify the arguments
    if members:
        element = "<members>{0}</members>".format(_build_members(members, True))
    else:
        ret.update({'comment': "The group members must be provided."})
        return ret

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

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

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

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

        result, msg = _edit_config(xpath, full_element)

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

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

    return ret