Ejemplo n.º 1
0
class ApiClient():
    """
    RestFull Client
    :param config:
    :type config:
    """
    def __init__(self, config, log=None):
        self.config = config
        self.log = log
        self.protocol = 'http'
        self.cache = mcache.Mcache()
        self.zauth_conn = ZmqREQConnection(ZmqFactory())
        self.zacct_conn = ZmqREQConnection(ZmqFactory())
        self.init_client().addCallback(self.log.info)
        reactor.callLater(
            1.0,
            self.check_api,
        )

    @defer.inlineCallbacks
    def init_client(self):
        try:
            reqdata = apiutils.make_request(self.config.api.api_secret, **{})
            resp = yield self.send_http(
                "%s/radagent/fetch" % self.config.api.api_url, reqdata)
            auth_endpoints = [
                ZmqEndpoint('connect', e)
                for e in resp.get('auth_endpoints', '').split(",")
            ]
            acct_endpoints = [
                ZmqEndpoint('connect', e)
                for e in resp.get('acct_endpoints', '').split(",")
            ]

            self.api_auth_url = resp.get("api_auth_url")
            self.api_acct_url = resp.get("api_acct_url")

            self.log.info('auth_endpoints: %s' % auth_endpoints)
            self.log.info('acct_endpoints: %s' % acct_endpoints)
            self.log.info('api_auth_url: %s' % self.api_auth_url)
            self.log.info('api_acct_url: %s' % self.api_acct_url)

            self.protocol = resp.get('protocol')

            self.zauth_conn.addEndpoints(auth_endpoints)
            self.zacct_conn.addEndpoints(acct_endpoints)

            defer.returnValue("ApiClient init done")

        except Exception as err:
            import traceback
            traceback.print_exc()
            self.log.error(u"[ApiClient] :: fetch radagent failure, %s" %
                           safeunicode(err.message))
            defer.returnValue("ApiClient init error")

    def check_api(self):
        try:
            self.log.info("ping api service")
            retry = lambda err: self.init_client().addCallback(self.log.info)
            msg = apiutils.make_request(self.config.api.api_secret,
                                        action='ping')
            self.send_zmq('authorize', msg).addErrback(retry)
            self.send_zmq('accounting', msg).addErrback(retry)
        except Exception as err:
            self.log.error("ping api service error, %s" %
                           apiutils.safeunicode(err.message))

        reactor.callLater(
            30.0,
            self.check_api,
        )

    @defer.inlineCallbacks
    def send_http(self, apiurl, reqdata):
        """ send radius request
        :param apiurl: oss server api
        :param reqdata: json data
        """
        try:
            if self.config.defaults.debug:
                self.log.debug(
                    "[HttpClient] :: Send http request to {0}, {1}".format(
                        safestr(apiurl), safeunicode(reqdata)))

            headers = {"Content-Type": ["application/json;charset=utf-8"]}
            resp = yield requests.post(safestr(apiurl),
                                       data=reqdata,
                                       headers=headers)

            if resp.code != 200:
                defer.returnValue(
                    dict(
                        code=1,
                        msg=u'server return error http status code {0}'.format(
                            resp.code)))
            else:
                result = yield resp.json()
                if self.config.defaults.debug:
                    self.log.debug(
                        "[HttpClient] :: Received http response from {0}, {1}".
                        format(safestr(apiurl), safeunicode(result)))
                if result.get('code') == 0 and not apiutils.check_sign(
                        self.config.api.api_secret, result):
                    defer.returnValue(dict(code=1, msg=u"sign error"))
                else:
                    defer.returnValue(result)
        except Exception as err:
            import traceback
            traceback.print_exc()
            defer.returnValue(dict(code=1, msg=u'server error'))

    @defer.inlineCallbacks
    def send_zmq(self, rtype, reqdata):
        """ send radius request
        :param rtype: radius msg type
        :param reqdata: json data
        """
        try:
            if self.config.defaults.debug:
                self.log.debug("[ZmqClient] :: Send zmq request {0}".format(
                    safestr(reqdata)))

            resp = None
            if rtype == 'authorize':
                resp = yield self.zauth_conn.sendMsg(reqdata, timeout=15.0)
            elif rtype == 'accounting':
                resp = yield self.zacct_conn.sendMsg(reqdata, timeout=15.0)
            else:
                defer.returnValue(
                    dict(code=1, msg=u"not support radius msg type"))

            if self.config.defaults.debug:
                self.log.debug(
                    "[ZmqClient] :: Received zmq response {0}".format(
                        safeunicode(resp)))

            result = json.loads(resp[0])
            if not apiutils.check_sign(self.config.api.api_secret, result):
                defer.returnValue(dict(code=1, msg=u"zmq msg sign error"))

            defer.returnValue(result)
        except Exception as err:
            import traceback
            traceback.print_exc()
            defer.returnValue(dict(code=1, msg=u'server error'))

    @defer.inlineCallbacks
    def get_nas(self, nasaddr, nas_identify=None):
        _start = time.clock()
        cache_key = "toughengine.get_nas.{0}".format(nasaddr)
        if cache_key in self.cache.cache:
            log.msg("[get_nas cast:%.6f second (hit cache)]" %
                    (time.clock() - _start))
            defer.returnValue(self.cache.get(cache_key))

        api_url = self.config.api.api_url
        if not api_url:
            raise ValueError("api_url is None")

        try:
            _reqdata = dict(
                nasaddr=nasaddr,
                nas_identify=nas_identify,
            )
            reqdata = apiutils.make_request(self.config.api.api_secret,
                                            _reqdata)
            resp = yield self.send_http("%s/nas/fetch" % api_url, reqdata)
            if resp['code'] == 0:
                self.cache.set(cache_key, resp, expire=120)
            self.log.info("[init_nas cast:%.6f second]" %
                          (time.clock() - _start))
            defer.returnValue(resp)
        except Exception as err:
            import traceback
            traceback.print_exc()
            self.log.error(u"[HttpClient] :: fetch nas failure,%s" %
                           safestr(err.message))
            defer.returnValue(
                dict(code=1, msg=u"fetch nas error, please see log detail"))

    @defer.inlineCallbacks
    def authorize(self,
                  username,
                  domain,
                  macaddr,
                  nasaddr,
                  vlanid1,
                  vlanid2,
                  textinfo=None):
        """send radius auth request
        :param username: not contain @doamin
        :param domain:
        :param macaddr:
        :param nasaddr:
        :param vlanid1:
        :param vlanid2:
        :param textinfo:
        """
        _start = time.clock()
        try:
            _reqdata = dict(username=username,
                            domain=safestr(domain),
                            macaddr=safestr(macaddr),
                            nasaddr=nasaddr,
                            vlanid1=vlanid1,
                            vlanid2=vlanid2,
                            textinfo=safestr(textinfo))
            reqdata = apiutils.make_request(self.config.api.api_secret,
                                            _reqdata)
            if self.protocol == 'http':
                resp = yield self.send_http(self.api_auth_url, reqdata)
                self.log.info("[http authorize cast:%.6f second]" %
                              (time.clock() - _start))
                defer.returnValue(resp)
            elif self.protocol == 'zmq':
                resp = yield self.send_zmq('authorize', reqdata)
                self.log.info("[zmq authorize cast:%.6f second]" %
                              (time.clock() - _start))
                defer.returnValue(resp)
            else:
                defer.returnValue(dict(code=1, msg=u"not support"))

        except Exception as err:
            import traceback
            traceback.print_exc()
            self.log.error(u"[ApiClient] :: authorize failure,%s" %
                           safestr(err.message))
            defer.returnValue(
                dict(code=1, msg=u"authorize error, please see log detail"))

    @defer.inlineCallbacks
    def accounting(self,
                   req_type,
                   username,
                   session_id,
                   session_time,
                   session_timeout,
                   macaddr,
                   nasaddr,
                   ipaddr,
                   input_octets,
                   output_octets,
                   input_pkts,
                   output_pkts,
                   nas_port,
                   event_timestamp,
                   nas_port_type,
                   nas_port_id,
                   textinfo=None):
        """send radius accounting request
        :param req_type: 1 Start 2 Stop 3 Alive
        :param username:
        :param session_id:
        :param session_time:
        :param session_timeout:
        :param macaddr:
        :param nasaddr:
        :param ipaddr:
        :param input_octets:
        :param output_octets:
        :param input_pkts:
        :param output_pkts:
        :param textinfo:
        """
        _start = time.clock()
        try:
            _reqdata = dict(req_type=req_type,
                            username=username,
                            session_id=session_id,
                            session_time=session_time,
                            session_timeout=session_timeout,
                            macaddr=macaddr,
                            nasaddr=nasaddr,
                            ipaddr=ipaddr,
                            input_octets=input_octets,
                            output_octets=output_octets,
                            input_pkts=input_pkts,
                            output_pkts=output_pkts,
                            nas_port=nas_port,
                            event_timestamp=event_timestamp,
                            nas_port_type=nas_port_type,
                            nas_port_id=nas_port_id,
                            textinfo=safestr(textinfo))
            reqdata = apiutils.make_request(self.config.api.api_secret,
                                            _reqdata)
            if self.protocol == 'http':
                resp = yield self.send_http(self.api_acct_url, reqdata)
                self.log.info("[http accounting cast:%.6f second]" %
                              (time.clock() - _start))
                defer.returnValue(resp)
            elif self.protocol == 'zmq':
                resp = yield self.send_zmq('accounting', reqdata)
                self.log.info("[zmq accounting cast:%.6f second]" %
                              (time.clock() - _start))
                defer.returnValue(resp)
            else:
                defer.returnValue(dict(code=1, msg=u"not support"))
        except Exception as err:
            self.log.error(u"[HttpClient] :: accounting failure,%s" %
                           safestr(err.message))
            defer.returnValue(
                dict(code=1, msg=u"accounting error, please see log detail"))