Cisco pxgrid部分函数封装

pxgrid.py

import requests
import urllib3
import os
import sys
import json
import websockets
import asyncio
from io import StringIO
from base64 import b64encode
import ssl

urllib3.disable_warnings()
headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
}


def gen_http_basic_auth(username, password):
    if isinstance(username, str):
        username = username.encode('latin1')
    if isinstance(password, str):
        password = password.encode('latin1')
    return 'Basic ' + b64encode(b':'.join((username, password))).decode().strip()


class Config(object):
    def __init__(
            self, hostname, username, password=None, description=None,
            client_cert=None, client_key=None, client_key_password=None, server_cert=None
    ):
        """
        :param hostname: ISE server
        :param username: client_node_name
        :param password: password (optional)
        :param description: Description (optional)
        :param client_cert: Client certificate chain filename (optional)
        :param client_key: Client key filename (optional)
        :param client_key_password: Client key password (optional)
        :param server_cert: Server certificates filename
        """
        self.hostname = hostname
        self.username = username
        self.password = password
        self.description = description
        self.client_cert = client_cert
        self.client_key = client_key
        self.client_key_password = client_key_password
        self.server_cert = server_cert

    def get_ssl_context(self):
        context = ssl.create_default_context()
        if self.client_cert is not None:
            context.load_cert_chain(
                certfile=self.client_cert,
                keyfile=self.client_key,
                password=self.client_key_password
            )
        context.load_verify_locations(cafile=self.server_cert)
        return context


class StompFrame:
    def __init__(self):
        self.headers = {}
        self.command = None
        self.content = None

    def get_command(self):
        return self.command

    def set_command(self, command):
        self.command = command

    def get_content(self):
        return self.content

    def set_content(self, content):
        self.content = content

    def get_header(self, key):
        return self.headers[key]

    def set_header(self, key, value):
        self.headers[key] = value

    def write(self, out):
        out.write(self.command)
        out.write('\n')
        for key in self.headers:
            out.write(key)
            out.write(':')
            out.write(self.headers[key])
            out.write('\n')
        out.write('\n')
        if self.content is not None:
            out.write(self.content)
        out.write('\0')

    @staticmethod
    def parse(_input):
        frame = StompFrame()
        frame.command = _input.readline().rstrip('\r\n')
        for line in _input:
            line = line.rstrip('\r\n')
            if line == '':
                break
            (name, value) = line.split(':')
            frame.headers[name] = value
        frame.content = _input.read()[:-1]
        return frame


class WebSocketStomp:
    def __init__(self, ws_url, user, password, ssl_ctx):
        self.ws_url = ws_url
        self.user = user
        self.password = password
        self.ssl_ctx = ssl_ctx
        self.ws = None

    async def connect(self):
        authorization = gen_http_basic_auth(self.user, self.password)
        self.ws = await websockets.connect(
            uri=self.ws_url, extra_headers={
                'Authorization': authorization
            }, ssl=self.ssl_ctx
        )

    async def stomp_connect(self, hostname):
        print('STOMP CONNECT host=' + hostname)
        frame = StompFrame()
        frame.set_command("CONNECT")
        frame.set_header('accept-version', '1.2')
        frame.set_header('host', hostname)
        out = StringIO()
        frame.write(out)
        await self.ws.send(out.getvalue().encode())

    async def stomp_subscribe(self, topic):
        print('STOMP SUBSCRIBE topic=' + topic)
        frame = StompFrame()
        frame.set_command("SUBSCRIBE")
        frame.set_header('destination', topic)
        frame.set_header('id', 'my-id')
        out = StringIO()
        frame.write(out)
        await self.ws.send(out.getvalue().encode())

    async def stomp_send(self, topic, message):
        print('STOMP SEND topic=' + topic)
        frame = StompFrame()
        frame.set_command("SEND")
        frame.set_header('destination', topic)
        frame.set_header('content-length', str(len(message)))
        frame.set_content(message)
        out = StringIO()
        frame.write(out)
        await self.ws.send(out.getvalue().encode())

    # only returns for MESSAGE
    async def stomp_read_message(self):
        while True:
            message = await self.ws.recv()
            s_in = StringIO(message.decode())
            stomp = StompFrame.parse(s_in)
            if stomp.get_command() == 'MESSAGE':
                return stomp.get_content()
            elif stomp.get_command() == 'CONNECTED':
                version = stomp.get_header('version')
                print('STOMP CONNECTED version=' + version)
            elif stomp.get_command() == 'RECEIPT':
                receipt = stomp.get_header('receipt-id')
                print('STOMP RECEIPT id=' + receipt)
            elif stomp.get_command() == 'ERROR':
                print('STOMP ERROR content=' + stomp.get_content())

    async def stomp_disconnect(self, receipt=None):
        print('STOMP DISCONNECT receipt=' + receipt)
        frame = StompFrame()
        frame.set_command("DISCONNECT")
        if receipt is not None:
            frame.set_header('receipt', receipt)
        out = StringIO()
        frame.write(out)
        await self.ws.send(out.getvalue().encode())

    async def disconnect(self):
        await self.ws.close()

    def is_open(self):
        return self.ws.open


class PXGridProviderAPI(object):
    __doc__ = (
        'pxGrid nodes connect to pxGrid Controller to perform control operations that facilitates communications '
        'between consumer nodes and provider nodes. '
        'This guide discuss HTTP APIs use by provider nodes'
    )
    SERVICE_NAME = None
    WS_PUBSUB_SERVICE = 'com.cisco.ise.pubsub'

    def __init__(self, config):
        self.config = config
        if self.SERVICE_NAME is not None:
            services = self.service_lookup(self.SERVICE_NAME).get('services')
            self.service = services[0]
            self.service_node_name = self.service['nodeName']
            self.rest_base_url = self.service['properties']['restBaseUrl']
            self.service_secret = self.access_secret(self.service_node_name).get('secret')
            # PubSub WSS
            self.pubsub_services = self.service_lookup(self.WS_PUBSUB_SERVICE).get('services')
            if self.pubsub_services:
                self.pubsub_service = self.pubsub_services[0]
                self.pubsub_node_name = self.pubsub_service.get('nodeName')
                self.pubsub_secret = self.access_secret(self.pubsub_node_name).get('secret')
                self.pubsub_ws_url = self.pubsub_service.get('properties').get('wsUrl')

    def _send_request(self, url_suffix, payload, ps_url_suffix=True, **others):
        if ps_url_suffix:
            url_suffix = url_suffix.title().replace('_', '')
        url = f'https://{self.config.hostname}:8910/pxgrid/control/{url_suffix}'
        kwargs = dict(
            url=url, verify=False, json=payload, auth=(self.config.username, self.config.password)
        )
        kwargs.update(**others)
        res = requests.post(headers=headers, **kwargs)
        result = {
            'status_code': res.status_code
        }
        try:
            result.update(res.json())
        except:
            pass
        return result

    def _send_query(self, url_suffix, payload, ps_url_suffix=True):
        if ps_url_suffix:
            url_suffix = ''.join([url_suffix.split('_')[0]] + [word.title() for word in url_suffix.split('_')[1:]])

        url = os.path.join(self.rest_base_url, url_suffix).replace('\\', '/')
        kwargs = dict(
            url=url, verify=False, json=payload, auth=(self.config.username, self.service_secret)
        )
        res = requests.post(headers=headers, **kwargs)
        result = {
            'status_code': res.status_code
        }
        try:
            result.update(res.json())
        except:
            pass
        return result

    def service_register(self, **payload):
        """
        ServiceRegister is used to register a service name with its properties and operations.
        name is a string (alphanumerics and periods(.) of max 50 characters)
        properties will be sent to the consumers who is looking up for the service.
        It is an array of name and value pairs. name is a string (alphanumerics of max 50 characters).
        value is a string (max 100 characters).

        operations are array of service and operation pairs this provider will used for Authorization API.
        This lets the pxGrid controller knows the possible combinations of services and operations, in which can be
        used to setup rules. service is a string (alphanumerics and periods(.) of max 50 characters). operation is a
        string (alphanumerics of max 50 characters).

        ServiceRegister returns id and a reregisterTimeMillis.
        id is used for subsequence ServiceReregister and ServiceUnregister commands.

        reregisterTimeMillis is the time in milliseconds that this service must re-register in order
        to keep the service alive. Otherwise, pxGrid Controller will assume the service is no long available
        and remove the entry.
        :param payload: {
            "name": "com.cisco.ise.config.anc",
            "properties":
            {
                "restBaseUrl":"https://pxgrid-041.cisco.com:8910/pxgrid/ise/config/anc",
                "wsPubsubService":"com.cisco.ise.pubsub",
                "statusTopic":"/topic/com.cisco.ise.config.anc.status"
            },
            "operations":[
              {
                 "service": "com.cisco.ise.config.anc",
                 "operation": "gets"
              },
              {
                 "service": "com.cisco.ise.config.anc",
                 "operation": "sets"
              },
              {
                 "service": "com.cisco.ise.pubsub",
                 "operation": "publish /topic/com.cisco.ise.config.anc.status"
              },
              {
                 "service": "com.cisco.ise.pubsub",
                 "operation": "subscribe /topic/com.cisco.ise.config.anc.status"
              }
            ]
        }
         or {
            "id":"123"
        }
        :return:
        {
            "id":"123",
            "reregisterTimeMillis":60000
        } or {}
        """
        return self._send_request(sys._getframe().f_code.co_name, payload)

    def service_reregister(self, **payload):
        """
        ServiceReregister is used to signify that the service is still active.
        This must be periodically triggered according to the reregisterTimeMillis
        @:param payload: {
            "id":"123"
        }
        :return: nothing
        """
        return self._send_request(sys._getframe().f_code.co_name, payload)

    def service_unregister(self, **payload):
        """
        ServiceUnregister is used to un-register a service.
        :param payload: {
            "id":"123"
        }
        :return: {}
        """
        return self._send_request(sys._getframe().f_code.co_name, payload)

    def authorization(self, **payload):
        """
        @:param payload: {
            "requestNodeName":"node1",
            "serviceName":"com.cisco.ise.config.anc",
            "serviceOperation":"gets"
        }
        :return: {
            "authorization":"PERMIT"
        }
        """
        return self._send_request(sys._getframe().f_code.co_name, payload)

    def access_secret(self, peer_node_name):
        """
        :param peer_node_name:
        :return: {
            "secret":"oWhgNC7oNpaulpJ6"
        }
        """
        payload = {'peerNodeName': peer_node_name}
        return self._send_request(sys._getframe().f_code.co_name, payload)

    def account_create(self, username):
        """
        :param username: python_test
        :return: {"nodeName": "python_test", "password": "QVTXCDgoQlbspqmQ", "userName": "python_test"}
        """
        payload = {'nodeName': username}
        return self._send_request(sys._getframe().f_code.co_name, payload)

    def account_activate(self):
        """
        Request
        { "description": string (max 50 characters. Alphanumerics, spaces, underscores(_), comma(,),
            dashes(-) and periods(.))
        }

        Response
        { "accountState": string (PENDING,DISABLE or ENABLED) "version": string (e.g "2.0.2.1") }
        :return: { "accountState": string (PENDING,DISABLE or ENABLED) "version": string (e.g "2.0.2.1") }
        """
        payload = {}
        if self.config.description is not None:
            payload['description'] = self.config.description
        return self._send_request(sys._getframe().f_code.co_name, payload)

    def service_lookup(self, name):
        payload = {'name': name}
        return self._send_request(sys._getframe().f_code.co_name, payload)

    @classmethod
    async def future_read_message(cls, ws, future):
        try:
            message = await ws.stomp_read_message()
            future.set_result(message)
        except websockets.ConnectionClosed:
            print('Websocket connection closed')

    async def subscribe_loop(self, topic, callback):
        ws = WebSocketStomp(
            ws_url=self.pubsub_ws_url,
            user=self.config.username,
            password=self.pubsub_secret,
            ssl_ctx=self.config.get_ssl_context()
        )
        await ws.connect()
        await ws.stomp_connect(self.pubsub_node_name)
        await ws.stomp_subscribe(topic)
        print("Ctrl-C to disconnect...")
        while True:
            future = asyncio.Future()
            future_read = self.future_read_message(ws, future)
            try:
                await asyncio.wait([future_read], return_when=asyncio.FIRST_COMPLETED)
            except asyncio.CancelledError:
                await ws.stomp_disconnect('123')
                # wait for receipt
                await asyncio.sleep(0.05)
                await ws.disconnect()
                future.cancel()
                break
            else:
                message = json.loads(future.result())
                callback(message)


class ANCConfiguration(PXGridProviderAPI):
    __doc__ = 'This is Adaptive Network Control configuration service'
    SERVICE_NAME = 'com.cisco.ise.config.anc'
    ANC_STATUS_TOPIC = '/topic/com.cisco.ise.config.anc.status'

    def get_policies(self):
        """
        This is used to get policies.
        An empty json structure must be sent as the request. If no policy is found, policies will have an empty array.
        :return: {
          "policies": [
            array of policy object
          ]
        }
        """
        payload = {}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_policy_by_name(self, name):
        """
        If policy does not exist, HTTP status "204 No content" will be returned with empty body
        :param name: 'Quarantine'
        :return: {
          policy object
        }
        """
        payload = {'name': name}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def create_policy(self, policy):
        """
        here is no need to set the "id" field for the request policy object.
        After successful creation, the "id" field will be populated in the returned policy object
        If the policy name is already used in an existing policy, HTTP status "409 Conflict" will be returned.
        :param policy: {
          policy object
        }
        :return:
        """
        payload = {'policy ': policy}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def delete_policy_by_name(self, name):
        """
        This is no response body for this query. If policy is deleted, HTTP status "200" will be returned.
        If policy does not exist, HTTP status "204 No content" will be returned.
        :param name :
        :return: (empty)
        """
        payload = {'name ': name}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_endpoints(self):
        """
        This is used to get endpoints with policies applied
        An empty json structure must be sent as the request.
        If no endpoint policy is found, endpointPolicies will have an empty array.
        :return: {
          "endpoints": [
            array of endpoint object
          ]
        }
        """
        payload = {}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_endpoint_by_mac_address(self, mac_address):
        """
        If endpoint does not exist, HTTP status "204 No content" will be returned.
        :param mac_address:
        :return: {
          endpoint object
        }
        """
        payload = {'macAddress': mac_address}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def apply_endpoint_by_ip_address(self, policy_name, ip_address):
        """
        Apply a policy to the endpoint using IP Address. If endpoint already has existing policy applied,
        the return status will be FAILURE with reason "mac address is already associated with this policy".
        If endpoint does not exist, the return status will be FAILURE with reason "Session lookup failure".
        :param policy_name:
        :param ip_address:
        :return: {
          status object
        }
        """
        payload = {
            "policyName": policy_name,
            "ipAddress": ip_address
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def apply_endpoint_by_mac_address(self, policy_name, mac_address):
        """
        Apply a policy to the endpoint using MAC Address. If endpoint already has existing policy applied,
        the return status will be FAILURE with reason "mac address is already associated with this policy".
        :param policy_name:
        :param mac_address:
        :return: {
          status object
        }
        """
        payload = {
            "policyName": policy_name,
            "macAddress": mac_address
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def clear_endpoint_by_mac_address(self, mac_address):
        """
        Apply a policy to the endpoint using MAC Address If endpoint does not have an existing policy applied,
        the return status will be FAILURE with reason "mac address is not associated with a policy".
        :param mac_address:
        :return: {
          status object
        }
        """
        payload = {
            "macAddress": mac_address
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_operation_status(self, operation_id):
        """
        If operation does not exist, HTTP status "204 No content" will be returned.
        :param operation_id:
        :return: {
          status object
        }
        """
        payload = {
            "operationId": operation_id
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)


class EndpointAsset(PXGridProviderAPI):
    __doc__ = (
        'This is ISE pxGrid Context-In feature This is for providers to publish asset data into ISE '
        'where ISE Profiler component acts as a subscriber to collect'
    )
    SERVICE_NAME = 'com.cisco.endpoint.asset'
    ENDPOINT_ASSET_TOPIC = '/topic/com.cisco.endpoint.asset'


class Endpoint(PXGridProviderAPI):
    __doc__ = (
        'This is ISE pxGrid Context-In feature This is for providers to publish asset data into ISE '
        'where ISE Profiler component acts as a subscriber to collect'
    )
    SERVICE_NAME = 'com.cisco.ise.endpoint'
    ENDPOINT_TOPIC = '/topic/com.cisco.ise.endpoint'


class MDM(PXGridProviderAPI):
    __doc__ = 'This is Mobile Device Management (MDM) service'
    SERVICE_NAME = 'com.cisco.ise.mdm'
    MDM_ENDPOINT_TOPIC = '/topic/com.cisco.ise.mdm.endpoint'

    def get_endpoints(self, _filter=None):
        """
        This is used to get endpoints.
        An optional filter can be used in which entries matching the attributes specified will be returned.
        If no filter is used, an empty json structure must be sent as the request.
        If no policy is found, endpoints will have an empty array.
        :param _filter:
        :return: {
          "endpoints": [
            array of endpoint object
          ]
        }
        """
        payload = {}
        if _filter is not None:
            payload = {
                'filter': _filter
            }
        return self._send_query(sys._getframe().f_code.co_name, payload=payload)

    def get_endpoint_by_mac_address(self, mac_address):
        """
        If not found, HTTP status "204 No content" will be returned.
        :param mac_address:
        :return: {
          endpoint object
        }
        """
        payload = {
            "macAddress": mac_address
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_endpoints_by_type(self, endpoint_type):
        """
        This is used to get endpoints by type, either non-compliant, registered or disconnected endpoints.
        If no policy is found, endpoints will have an empty array.
        :param endpoint_type: (required. values: NON_COMPLIANT, REGISTERED, DISCONNECTED)
        :return: {
          "endpoints": [
            array of endpoint object
          ]
        }
        """
        payload = {
            "type": endpoint_type
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_endpoints_by_os_type(self, os_type):
        """
        This is used to get endpoints by OS type, either ANDROID, IOS or WINDOWS.
        If no policy is found, endpoints will have an empty array.
        :param os_type: (required. values: ANDROID, IOS or WINDOWS)
        :return: {
          "endpoints": [
            array of endpoint object
          ]
        }
        """
        payload = {
            "osType": os_type
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)


class ProfilerConfiguration(PXGridProviderAPI):
    __doc__ = 'This is ISE Profiler configuration'
    SERVICE_NAME = 'com.cisco.ise.config.profiler'
    PROFILER_TOPIC = '/topic/com.cisco.ise.config.profiler'

    def get_profiles(self):
        """
        :return: {
          "profiles": [
            array of profile object
          ]
        }
        """
        payload = {}

        return self._send_query(sys._getframe().f_code.co_name, payload)


class RadiusFailure(PXGridProviderAPI):
    __doc__ = 'This service provides information about Radius protocol. RFC2865(https://tools.ietf.org/html/rfc2865)'
    SERVICE_NAME = 'com.cisco.ise.radius'
    RADIUS_FAILURE_TOPIC = '/topic/com.cisco.ise.radius.failure'

    def get_failures(self, start_timestamp=None):
        """
        This gets the Radius authentication failures since startTimestamp If startTimestamp is not specified,
        the failures for the last hour is returned.

        The failures included are ISE syslog message codes 5400 to 5499 that are listed here
        If no failure is found, failures attribute will contain an empty array.
        Note that radius authentication failures can be suppressed, so the timestamps being reported may not be
        the latest events
        :param start_timestamp: '2020-09-11T11:48:32.518+08:00' (optional)
        :return: {
          "failures": [
            array of failure objects
          ]
        }
        """
        payload = {}
        if start_timestamp is not None:
            payload['start_timestamp'] = start_timestamp
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_failure_by_id(self, _id):
        """
        This gets the failure object with the specified id.
        If not found, HTTP status "204 No content" will be returned.
        Note that radius authentication failures can be suppressed, so the timestamps being reported may not be the
        latest events.
        @:param _id:
        :return: {
          failure objects
        }
        """
        payload = {'id': _id}
        return self._send_query(sys._getframe().f_code.co_name, payload)


class SessionDirectory(PXGridProviderAPI):
    __doc__ = '''
        This service provides access to ISE Session Directory. There are 2 objects that can be accessed:
            Session
            UserGroup
        UserGroup is being separated out from Session object because of its size and static nature
    '''
    SERVICE_NAME = 'com.cisco.ise.session'
    SESSION_TOPIC = '/topic/com.cisco.ise.session'
    SESSION_GROUP_TOPIC = '/topic/com.cisco.ise.session.group'

    def get_sessions(self, start_timestamp=None):
        """
        This query returns a session object if found. If not, HTTP status "204 No content" will be returned.
        :param start_timestamp: '2020-09-11T11:48:32.518+08:00' (optional)
        :return: {
          "sessions": [
            array of session objects
          ]
        }
        """
        payload = {}
        if start_timestamp is not None:
            payload = {'startTimestamp': start_timestamp}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_session_by_ip_address(self, ip_address):
        """
        This query returns a session object if found. If not, HTTP status "204 No content" will be returned.
        :param ip_address:
        :return: {
          session object
        }
        """
        payload = {
            'ipAddress': ip_address,
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_session_by_mac_address(self, mac_address):
        """
        This query returns a session object if found. If not, HTTP status "204 No content" will be returned.
        :param mac_address: FC:FC:48:98:E2:79
        :return: {
          session object
        }
        """
        payload = {
            'macAddress': mac_address,
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_user_groups(self):
        """
        This gets all user groups. These user groups represent only the groups of
        the users that have been authenticated by ISE.
        If no user group is found, userGroups will have an empty array.
        :return: {
          "userGroups": [
            array of userGroup objects
          ]
        }
        """
        payload = {}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_user_group_by_username(self, username):
        """
        This query returns an array of group object.
        If the user has no group association, groups will have an empty array.
        If user does not exist, HTTP status "204 No content" will be returned.
        :param username:
        :return: {
          "groups": [
            array of group objects
          ]
        }
        """
        payload = {
            'userName': username
        }
        return self._send_query(sys._getframe().f_code.co_name, payload)


class SystemHealth(PXGridProviderAPI):
    __doc__ = 'This is ISE System Health service'
    SERVICE_NAME = 'com.cisco.ise.system'

    def get_healths(self, node_name=None, start_timestamp=None):
        """
        @:param node_name: string (optional) // All nodes if not present
        @:param start_timestamp: ISO8601 Datetime (optional) // Last 1 hour if not present
        '2020-09-11T17:00:32.518+08:00'
        :return: {
          "healths": [
            array of syshealth objects //
            {
             "timestamp":"2017-05-10T15:21:14.294-07:00",
             "serverName":"pxgrid-001",
             "ioWait":0.3,
             "cpuUsage":41.93,
             "memoryUsage":71.72,
             "diskUsageRoot":14.0,
             "diskUsageOpt":18.0,
             "loadAverage":2.1,
             "networkSent":2587,
             "networkReceived":93292
          },
          ]
        }
        """
        payload = {}
        if node_name:
            payload['nodeName'] = node_name
        if start_timestamp:
            payload['startTimestamp'] = start_timestamp
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_performances(self, node_name=None, start_timestamp=None):
        """
        @:param node_name: string (optional) // All nodes if not present
        @:param start_timestamp: ISO8601 Datetime (optional) // Last 1 hour if not present
        '2020-09-11T17:00:32.518+08:00'
        :return: {
          "performances": [ array of performance objects //
              {
                 "timestamp":"2017-05-10T18:06:15.011336-07:00",
                 "serverName":"pxgrid-001",
                 "radiusRate":0.0,
                 "radiusCount":0,
                 "radiusLatency":0.0
              },
          ]
        }
        """
        payload = {}
        if node_name:
            payload['nodeName'] = node_name
        if start_timestamp:
            payload['startTimestamp'] = start_timestamp
        return self._send_query(sys._getframe().f_code.co_name, payload)


class TrustSec(PXGridProviderAPI):
    __doc__ = 'This is ISE TrustSec service. Currently, it provides the status of SGACL downloads.'
    SERVICE_NAME = 'com.cisco.ise.trustsec'
    TRUSTSEC_POLICY_DOWNLOAD_TOPIC = '/topic/com.cisco.ise.trustsec.policy.download'


class TrustSecConfiguration(PXGridProviderAPI):
    __doc__ = 'This provides the configuration for TrustSec'
    SERVICE_NAME = 'com.cisco.ise.config.trustsec'
    TRUSTSEC_SECURITY_GROUP_TOPIC = '/topic/com.cisco.ise.config.trustsec.security.group'
    TRUSTSEC_SECURITY_GROUP_ACL_TOPIC = '/topic/com.cisco.ise.config.trustsec.security.group.acl'

    def get_security_groups(self, group_id=None):
        """
        The is used to get security groups. The security group id can be specified.
        If not specified, all security groups are returned.
        If no request parameter is used, an empty json structure must be sent.
        If no security group is found, securityGroups will have an empty array.
        :param group_id: (optional)
        :return: {
          "securityGroups": [
            array of securityGroup object
          ]
        }
        """
        payload = {}
        if group_id:
            payload = {'id': str(group_id)}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_security_group_acls(self, group_id=None):
        """
        The is used to get security group ACLs. The id for security group ACLs can be specified.
        If not specified, all security group ACLs are returned.

        If no request parameter is used, an empty json structure must be sent.
        If no security group ACL is found, securityGroupAcls will have an empty array.
        :param group_id: (optional)
        :return: {
          "securityGroupAcls": [
            array of securityGroupAcl object
          ]
        }
        """
        payload = {}
        if group_id:
            payload = {'id': str(group_id)}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_egress_policies(self):
        """
        This is used to get egress policies.
         An empty json structure must be sent as the request.
        If no egress policy is found, egressPolicies will have an empty array.
        :return: {
          "egressPolicies": [
            array of egressPolicy object
          ]
        }
        """
        payload = {}
        return self._send_query(sys._getframe().f_code.co_name, payload)

    def get_egress_matrices(self):
        """
        This is used to get egress policies.
        An empty json structure must be sent as the request.
        If no egress matrix is found, egressMatrices will have an empty array.
        :return: {
          "egressMatrices": [
            array of egressMatrix object
          ]
        }
        """
        payload = {}
        return self._send_query(sys._getframe().f_code.co_name, payload)


class TrustSecSXP(PXGridProviderAPI):
    __doc__ = 'This is ISE SXP service'
    SERVICE_NAME = 'com.cisco.ise.sxp'
    SXP_BINDING_TOPIC = '/topic/com.cisco.ise.sxp.binding'

    def get_bindings(self):
        """
        :return: {
          "bindings": [
            array of binding objects
          ]
        }
        """
        payload = {}
        return self._send_query(sys._getframe().f_code.co_name, payload)


class PxGrid(object):
    __doc__ = '''
        0.PXGridProviderAPI(Common):
            HTTP API:
                service_register(**payload) ServiceRegister用于注册具有其属性和操作的服务名称。
                                            名称是一个字符串(最多50个字符的字母数字和句点(。))
                                            属性将发送给正在寻找服务的消费者
                                            它是名称和值对的数组。 name是一个字符串(最多50个字符的字母数字)
                                            value是一个字符串(最多100个字符)
                service_reregister(**payload)
                service_unregister(**payload)
                access_secret()
                account_create()
                account_activate()
                service_lookup()
                
        
        1.ANCConfiguration: 自适应网络控制配置服务
            HTTP API: 
                get_policies()                                          获取所有策略
                get_policy_by_name(name)                                [required]获取名字为'name'的策略
                create_policy(policy)                                   [required]创建策略
                delete_policy_by_name(name)                             [required]删除策略
                get_endpoints()                                         获取所有endpoints
                get_endpoint_by_mac_address(mac_address)                [required]获取endpoint
                apply_endpoint_by_ip_address(policy_name, ip_address)   [required]使用IP地址对端点应用策略
                apply_endpoint_by_mac_address(policy_name, mac_address) [required]使用MAC地址对端点应用策略
                clear_endpoint_by_mac_address(mac_address)              [required]
                                                                        如果端点没有应用现有的策略,返回状态将是失败
                                                                        原因是MAC地址与策略不关联
                get_operation_status(operation_id)                      [required]获取操作
            WS STOMP:
                ANC_STATUS_TOPIC = '/topic/com.cisco.ise.config.anc.status'
        2.EndpointAsset: ISE pxGrid的上下文连接特性, 这是为提供者发布资产数据到ISE, 而ISE Profiler组件充当收集的订阅者
            WS STOMP:
                ENDPOINT_ASSET_TOPIC = '/topic/com.cisco.endpoint.asset' 
        3.MDM: 移动设备管理(MDM)服务
            HTTP API:
                get_endpoints(_filter)                    [optional]获取端点, 可以使用可选的过滤器, 返回与指定属性匹配的条目
                get_endpoint_by_mac_address(mac_address)  [required] 通过MAC地址获取端点
                get_endpoints_by_type(endpoint_type)      [required. values: NON_COMPLIANT, REGISTERED, DISCONNECTED]
                                                          按端点类型获取端点,即不符合,已注册或已断开连接的端点
                get_endpoints_by_os_type(os_type)         按操作系统类型(ANDROID,IOS或WINDOWS)获取端点
            WS STOMP:
                MDM_ENDPOINT_TOPIC = '/topic/com.cisco.ise.mdm.endpoint'
        4.ProfilerConfiguration: ISE Profiler配置
            HTTP API:
                get_profiles() 获取所有profiler配置
            WS STOMP:
                PROFILER_TOPIC = '/topic/com.cisco.ise.config.profiler'
        5.RadiusFailure: 该服务提供有关Radius协议的信息
            HTTP API:
                get_failures(start_timestamp)   [ISO8601 Datetime (optional)]
                                                获取startTimestamp以后的Radius身份验证失败的数据
                                                如果未指定start_timestamp, 则返回上一小时的失败的数据
                get_failure_by_id(_id)          [required]获取具有指定ID的失败对象
            WS STOMP:
                RADIUS_FAILURE_TOPIC = '/topic/com.cisco.ise.radius.failure'
        6.SessionDirectory: 此服务提供对ISE会话目录的访问
            HTTP API:
                get_sessions(start_timestamp)           [ISO8601 Datetime (optional)]
                                                        获取所有会话, 在请求中可以选择使用start_timestamp来获取比时间戳更新的会话
                get_session_by_ip_address(ip_address)   [required]获取指定IP地址的会话
                get_session_by_mac_address(mac_address) [required]获取指定MAC地址的会话
                get_user_groups()                       获取所有用户组
                get_user_group_by_username(username)    [required]获取指定用户的组
            WS STOMP:
                SESSION_TOPIC = '/topic/com.cisco.ise.session'
                SESSION_GROUP_TOPIC = '/topic/com.cisco.ise.session.group'
        7.SystemHealth: ISE系统健康服务
            HTTP API:
                get_healths(node_name, start_timestamp) [node_name(optional), start_timestamp(optional)]
        8.TrustSec: ISE TrustSec服务。 当前它提供SGACL下载的状态
            WS STOMP:
                TRUSTSEC_POLICY_DOWNLOAD_TOPIC = '/topic/com.cisco.ise.trustsec.policy.download'
        9.TrustSecConfiguration: 提供了TrustSec的配置
            HTTP API:
                get_security_groups(group_id)       [optional]用于获取安全组, 可以指定安全组ID。
                get_security_group_acls(group_id)   [optional]用于获取安全组ACL, 可以指定安全组ACL的ID。
                                                    如果未指定, 则返回所有安全组ACL
                get_egress_policies()               获取出口策略, egressPolicies
                get_egress_matrices()               获取出口策略, egressMatrices    
            WS STOMP:
                TRUSTSEC_SECURITY_GROUP_TOPIC = '/topic/com.cisco.ise.config.trustsec.security.group'
                TRUSTSEC_SECURITY_GROUP_ACL_TOPIC = '/topic/com.cisco.ise.config.trustsec.security.group.acl'
        10.TrustSecSXP: ISE SXP服务
            HTTP API:
                get_bindings()  
            WS STOMP:
                SXP_BINDING_TOPIC = '/topic/com.cisco.ise.sxp.binding'
        
    '''
    ANCConfiguration = ANCConfiguration
    EndpointAsset = EndpointAsset
    Endpoint = Endpoint
    MDM = MDM
    ProfilerConfiguration = ProfilerConfiguration
    RadiusFailure = RadiusFailure
    SessionDirectory = SessionDirectory
    SystemHealth = SystemHealth
    TrustSec = TrustSec
    TrustSecConfiguration = TrustSecConfiguration
    TrustSecSXP = TrustSecSXP

 

subscribe_test.py

from pxgrid import PxGrid, Config
import json
import time
import asyncio


def callback_session(message):
    print(json.dumps(message, indent=4))


def callback_radius_failure(message):
    print(json.dumps(message, indent=4))


def callback_endpoint(message):
    print(json.dumps(message, indent=4))


def test():
    anc = PxGrid.EndpointAsset(config)
    print(anc.get_endpoint_by_mac_address('34:AB:37:F2:8D:A2'))
    return
    endpoint = PxGrid.Endpoint(config)
    print(endpoint)
    loop = asyncio.get_event_loop()
    task_endpoint = asyncio.ensure_future(
        endpoint.subscribe_loop(topic=endpoint.ENDPOINT_TOPIC, callback=callback_radius_failure)
    )
    # Setup signal handlers
    # loop.add_signal_handler(signal.SIGINT, task_session.cancel)
    # loop.add_signal_handler(signal.SIGTERM, task_session.cancel)
    # Event loop
    tasks = [
        task_endpoint,
        # task_radius
    ]
    loop.run_until_complete(asyncio.wait(tasks))


def main():
    session = PxGrid.SessionDirectory(config=config)

    while session.account_activate()['accountState'] != 'ENABLED':
        time.sleep(60)

    loop = asyncio.get_event_loop()
    task_session = asyncio.ensure_future(
        session.subscribe_loop(topic=session.SESSION_TOPIC, callback=callback_session)
    )

    radius = PxGrid.RadiusFailure(config=config)

    task_radius = asyncio.ensure_future(
        radius.subscribe_loop(topic=radius.RADIUS_FAILURE_TOPIC, callback=callback_radius_failure)
    )
    # Setup signal handlers
    # loop.add_signal_handler(signal.SIGINT, task_session.cancel)
    # loop.add_signal_handler(signal.SIGTERM, task_session.cancel)
    # Event loop
    tasks = [
        task_session,
        # task_radius
    ]
    loop.run_until_complete(asyncio.wait(tasks))


if __name__ == '__main__':
    config = Config(
        hostname='10.96.83.245',
        username='python_test',
        password='password',
        description='python test',
        server_cert=r'certs/CertificateServicesRootCA-ISE-PAN-CNBJ_.cer'
    )
    test()

 

posted @ 2021-01-06 18:02  士为知己  阅读(118)  评论(0)    收藏  举报