runtime/WampClient.py
branchnevow_service_rework
changeset 2218 7a4deed94eb2
parent 2215 f808ec7dc10e
child 2220 985f234b0d09
equal deleted inserted replaced
2217:a603d1ba287b 2218:7a4deed94eb2
    33 from autobahn.wamp import types, auth
    33 from autobahn.wamp import types, auth
    34 from autobahn.wamp.serializer import MsgPackSerializer
    34 from autobahn.wamp.serializer import MsgPackSerializer
    35 from twisted.internet.defer import inlineCallbacks
    35 from twisted.internet.defer import inlineCallbacks
    36 from twisted.internet.protocol import ReconnectingClientFactory
    36 from twisted.internet.protocol import ReconnectingClientFactory
    37 
    37 
       
    38 import runtime.NevowServer as NS
       
    39 
       
    40 from formless import annotate
    38 
    41 
    39 mandatoryConfigItems = ["ID", "active", "realm", "url"]
    42 mandatoryConfigItems = ["ID", "active", "realm", "url"]
    40 
    43 
    41 _transportFactory = None
    44 _transportFactory = None
    42 _WampSession = None
    45 _WampSession = None
    65 SubscribedEvents = []
    68 SubscribedEvents = []
    66 
    69 
    67 """ things to do on join (callables) """
    70 """ things to do on join (callables) """
    68 DoOnJoin = []
    71 DoOnJoin = []
    69 
    72 
       
    73 lastKnownConfig = None
    70 
    74 
    71 def GetCallee(name):
    75 def GetCallee(name):
    72     """ Get Callee or Subscriber corresponding to '.' spearated object path """
    76     """ Get Callee or Subscriber corresponding to '.' spearated object path """
    73     names = name.split('.')
    77     names = name.split('.')
    74     obj = _PySrv.plcobj
    78     obj = _PySrv.plcobj
    85         else:
    89         else:
    86             self.join(u"Automation")
    90             self.join(u"Automation")
    87 
    91 
    88     def onChallenge(self, challenge):
    92     def onChallenge(self, challenge):
    89         if challenge.method == u"wampcra":
    93         if challenge.method == u"wampcra":
    90             secret = self.config.extra["secret"].encode('utf8')
    94             if "secret" in self.config.extra:
    91             signature = auth.compute_wcs(secret, challenge.extra['challenge'].encode('utf8'))
    95                 secret = self.config.extra["secret"].encode('utf8')
    92             return signature.decode("ascii")
    96                 signature = auth.compute_wcs(secret, challenge.extra['challenge'].encode('utf8'))
       
    97                 return signature.decode("ascii")
       
    98             else:
       
    99                 raise Exception("no secret given for authentication")
    93         else:
   100         else:
    94             raise Exception("don't know how to handle authmethod {}".format(challenge.method))
   101             raise Exception("don't know how to handle authmethod {}".format(challenge.method))
    95 
   102 
    96     @inlineCallbacks
   103     @inlineCallbacks
    97     def onJoin(self, details):
   104     def onJoin(self, details):
   155             super(ReconnectingWampWebSocketClientFactory, self).clientConnectionFailed(connector, reason)
   162             super(ReconnectingWampWebSocketClientFactory, self).clientConnectionFailed(connector, reason)
   156         else:
   163         else:
   157             del connector
   164             del connector
   158 
   165 
   159 
   166 
       
   167 def CheckConfiguration(WSClientConf):
       
   168     url = WSClientConf["url"]
       
   169     if not IsCorrectUri(url):
       
   170         raise annotate.ValidateError(
       
   171             {"url":"Invalid URL: {}".format(url)},
       
   172             _("WAMP confiuration error:"))
       
   173 
   160 def GetConfiguration():
   174 def GetConfiguration():
       
   175     global lastKnownConfig
       
   176 
   161     WSClientConf = json.load(open(_WampConf))
   177     WSClientConf = json.load(open(_WampConf))
   162     for itemName in mandatoryConfigItems:
   178     for itemName in mandatoryConfigItems:
   163         if WSClientConf.get(itemName, None) is None :
   179         if WSClientConf.get(itemName, None) is None :
   164             raise Exception(_("WAMP configuration error : missing '{}' parameter.").format(itemName))
   180             raise Exception(_("WAMP configuration error : missing '{}' parameter.").format(itemName))
   165 
   181 
       
   182     CheckConfiguration(WSClientConf)
       
   183 
       
   184     lastKnownConfig = WSClientConf.copy()
   166     return WSClientConf
   185     return WSClientConf
   167 
   186 
   168 
   187 
   169 def SetConfiguration(WSClientConf):
   188 def SetConfiguration(WSClientConf):
   170     try:
   189     global lastKnownConfig
   171         with open(os.path.realpath(_WampConf), 'w') as f:
   190 
   172             json.dump(WSClientConf, f, sort_keys=True, indent=4)
   191     CheckConfiguration(WSClientConf)
   173         if 'active' in WSClientConf and WSClientConf['active']:
   192 
   174             if _transportFactory and _WampSession:
   193     lastKnownConfig = WSClientConf.copy()
   175                 StopReconnectWampClient()
   194     
   176             StartReconnectWampClient()
   195     with open(os.path.realpath(_WampConf), 'w') as f:
   177         else:
   196         json.dump(WSClientConf, f, sort_keys=True, indent=4)
       
   197     if 'active' in WSClientConf and WSClientConf['active']:
       
   198         if _transportFactory and _WampSession:
   178             StopReconnectWampClient()
   199             StopReconnectWampClient()
   179 
   200         StartReconnectWampClient()
   180         return WSClientConf
   201     else:
   181     except ValueError, ve:
   202         StopReconnectWampClient()
   182         print(_("WAMP save error: "), ve)
   203 
   183         return None
   204     return WSClientConf
   184     except Exception, e:
       
   185         print(_("WAMP save error: "), e)
       
   186         return None
       
   187 
   205 
   188 
   206 
   189 def LoadWampSecret(secretfname):
   207 def LoadWampSecret(secretfname):
   190     try:
   208     WSClientWampSecret = open(secretfname, 'rb').read()
   191         WSClientWampSecret = open(secretfname, 'rb').read()
   209     if len(WSClientWampSecret) == 0 :
   192         return WSClientWampSecret
   210         raise Exception(_("WAMP secret empty"))
   193     except ValueError, ve:
   211     return WSClientWampSecret
   194         print(_("Wamp secret load error:"), ve)
       
   195         return None
       
   196     except Exception:
       
   197         return None
       
   198 
   212 
   199 
   213 
   200 def IsCorrectUri(uri):
   214 def IsCorrectUri(uri):
   201     if re.match(r'w{1}s{1,2}:{1}/{2}.+:{1}[0-9]+/{1}.+', uri):
   215     return re.match(r'wss?://[^\s?:#-]+(:[0-9]+)?(/[^\s]*)?$', uri) is not None
   202         return True
       
   203     else:
       
   204         return False
       
   205 
   216 
   206 
   217 
   207 def RegisterWampClient(wampconf=None, wampsecret=None):
   218 def RegisterWampClient(wampconf=None, wampsecret=None):
   208     global _WampConf, _WampSecret
   219     global _WampConf, _WampSecret
   209     if wampsecret:
   220     if wampsecret:
   211     if wampconf:
   222     if wampconf:
   212         _WampConf = wampconf
   223         _WampConf = wampconf
   213 
   224 
   214     WSClientConf = GetConfiguration()
   225     WSClientConf = GetConfiguration()
   215 
   226 
   216     if not IsCorrectUri(WSClientConf["url"]):
       
   217         raise Exception(_("WAMP url {} is not correct!").format(WSClientConf["url"]))
       
   218 
       
   219     if not WSClientConf["active"]:
   227     if not WSClientConf["active"]:
   220         print(_("WAMP deactivated in configuration"))
   228         print(_("WAMP deactivated in configuration"))
   221         return
   229         return
   222 
   230 
   223     WampSecret = LoadWampSecret(_WampSecret)
   231     if _WampSecret is not None:
   224 
   232         WSClientConf["secret"] = LoadWampSecret(_WampSecret)
   225     if WampSecret is not None:
       
   226         WSClientConf["secret"] = WampSecret
       
   227 
   233 
   228     # create a WAMP application session factory
   234     # create a WAMP application session factory
   229     component_config = types.ComponentConfig(
   235     component_config = types.ComponentConfig(
   230         realm=WSClientConf["realm"],
   236         realm=WSClientConf["realm"],
   231         extra=WSClientConf)
   237         extra=WSClientConf)
   249         print(_("WAMP client can not connect to :"), WSClientConf["url"])
   255         print(_("WAMP client can not connect to :"), WSClientConf["url"])
   250         return False
   256         return False
   251 
   257 
   252 
   258 
   253 def StopReconnectWampClient():
   259 def StopReconnectWampClient():
   254     _transportFactory.stopTrying()
   260     if _transportFactory is not None :
   255     return _WampSession.leave()
   261         _transportFactory.stopTrying()
       
   262     if _WampSession is not None :
       
   263         _WampSession.leave()
   256 
   264 
   257 
   265 
   258 def StartReconnectWampClient():
   266 def StartReconnectWampClient():
   259     if _WampSession:
   267     if _WampSession:
   260         # do reconnect
   268         # do reconnect
   267 
   275 
   268 
   276 
   269 def GetSession():
   277 def GetSession():
   270     return _WampSession
   278     return _WampSession
   271 
   279 
   272 
   280 def getWampStatus():
   273 def StatusWampClient():
   281     if _transportFactory is not None :
   274     return _WampSession and _WampSession.is_attached()
   282         if _WampSession is not None :
       
   283             if _WampSession.is_attached() :
       
   284                 return "Attached"
       
   285             return "Established"
       
   286         return "Connecting"
       
   287     return "Disconnected"
   275 
   288 
   276 
   289 
   277 def SetServer(pysrv):
   290 def SetServer(pysrv):
   278     _PySrv = pysrv
   291     _PySrv = pysrv
       
   292 
       
   293 
       
   294 #### WEB CONFIGURATION INTERFACE ####
       
   295 
       
   296 webExposedConfigItems = ['active', 'url', 'ID']
       
   297 
       
   298 def wampConfigDefault(ctx,argument):
       
   299     if lastKnownConfig is not None :
       
   300         return lastKnownConfig.get(argument.name, None)
       
   301 
       
   302 def wampConfig(**kwargs):
       
   303     newConfig = lastKnownConfig.copy()
       
   304     for argname in webExposedConfigItems:
       
   305         newConfig[argname] = kwargs[argname]
       
   306 
       
   307     SetConfiguration(newConfig)
       
   308 
       
   309 webFormInterface = [
       
   310     ("status",
       
   311        annotate.String(label=_("Current status"),
       
   312                        immutable = True,
       
   313                        default = lambda *k:getWampStatus())),
       
   314     ("ID",
       
   315        annotate.String(label=_("ID"),
       
   316                        default = wampConfigDefault)),
       
   317     ("active",
       
   318        annotate.Boolean(label=_("Enable WAMP connection"),
       
   319                         default=wampConfigDefault)),
       
   320     ("url",
       
   321        annotate.String(label=_("WAMP Server URL"),
       
   322                        default=wampConfigDefault))]
       
   323 
       
   324 
       
   325 NS.ConfigurableSettings.addExtension(
       
   326     "wamp", 
       
   327     _("Wamp Settings"),
       
   328     webFormInterface,
       
   329     _("Set"),
       
   330     wampConfig)