EMANE  1.2.1
emane/shell/controlportclient.py
Go to the documentation of this file.
1 #
2 # Copyright (c) 2013-2014,2017-2018 - Adjacent Link LLC, Bridgewater,
3 # New Jersey
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in
14 # the documentation and/or other materials provided with the
15 # distribution.
16 # * Neither the name of Adjacent Link LLC nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 
34 from . import remotecontrolportapi_pb2
35 from . import ControlPortException
36 import socket
37 import sys
38 import select
39 import struct
40 import threading
41 import os
42 
43 def fromAny(any):
44  if any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_INT8:
45  return (any.i32Value,ControlPortClient.TYPE_INT8)
46  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_UINT8:
47  return (any.u32Value,ControlPortClient.TYPE_UINT8)
48  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_INT16:
49  return (any.i32Value,ControlPortClient.TYPE_INT16)
50  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_UINT16:
51  return (any.u32Value,ControlPortClient.TYPE_UINT16)
52  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_INT32:
53  return (any.i32Value,ControlPortClient.TYPE_INT32)
54  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_UINT32:
55  return (any.u32Value,ControlPortClient.TYPE_UINT32)
56  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_INT64:
57  return (any.i64Value,ControlPortClient.TYPE_INT64)
58  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_UINT64:
59  return (any.u64Value,ControlPortClient.TYPE_UINT64)
60  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_FLOAT:
61  return (any.fValue,ControlPortClient.TYPE_FLOAT)
62  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_DOUBLE:
63  return (any.dValue,ControlPortClient.TYPE_DOUBLE)
64  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_STRING:
65  return (any.sValue,ControlPortClient.TYPE_STRING)
66  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_BOOLEAN:
67  return (any.bValue,ControlPortClient.TYPE_BOOLEAN)
68  elif any.type == remotecontrolportapi_pb2.Any.TYPE_ANY_INETADDR:
69  return (any.sValue,ControlPortClient.TYPE_INETADDR)
70 
71 def toAny(any,value,valueType):
72 
73  if valueType == ControlPortClient.TYPE_INT8:
74  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_INT8
75  any.i32Value = value
76 
77  elif valueType == ControlPortClient.TYPE_UINT8:
78  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_UINT8
79  any.u32Value = value
80 
81  elif valueType == ControlPortClient.TYPE_INT16:
82  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_INT16
83  any.i32Value = value
84 
85  elif valueType == ControlPortClient.TYPE_UINT16:
86  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_UINT16
87  any.u32Value = value
88 
89  elif valueType == ControlPortClient.TYPE_INT32:
90  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_INT32
91  any.i32Value = value
92 
93  elif valueType == ControlPortClient.TYPE_UINT32:
94  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_UINT32
95  any.u32Value = value
96 
97  elif valueType == ControlPortClient.TYPE_INT64:
98  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_INT64
99  any.i64Value = value
100 
101  elif valueType == ControlPortClient.TYPE_UINT64:
102  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_UINT64
103  any.u64Value = value
104 
105  elif valueType == ControlPortClient.TYPE_FLOAT:
106  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_FLOAT
107  any.fValue = value
108 
109  elif valueType == ControlPortClient.TYPE_DOUBLE:
110  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_DOUBLE
111  any.dValue = value
112 
113  elif valueType == ControlPortClient.TYPE_STRING:
114  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_STRING
115  any.sValue = value
116 
117  elif valueType == ControlPortClient.TYPE_BOOLEAN:
118  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_BOOLEAN
119  any.bValue = value
120 
121  elif valueType == ControlPortClient.TYPE_INETADDR:
122  any.type = remotecontrolportapi_pb2.Any.TYPE_ANY_INETADDR
123  any.sValue = value
124 
125  return any
126 
127 class ControlPortClient:
128  TYPE_INT8 = 1
129  TYPE_UINT8 = 2
130  TYPE_INT16 = 3
131  TYPE_UINT16 = 4
132  TYPE_INT32 = 5
133  TYPE_UINT32 = 6
134  TYPE_INT64 = 7
135  TYPE_UINT64 = 8
136  TYPE_FLOAT = 9
137  TYPE_DOUBLE = 10
138  TYPE_STRING = 11
139  TYPE_BOOLEAN = 12
140  TYPE_INETADDR = 13
141 
142  def __init__(self,hostname,port,disconnect=None):
143  self._disconnect = disconnect
144  self._buffer = None
145  self._messageLengthBytes = 0
146  self._sock = socket.socket()
147  self._connected = True
148 
149  try:
150  self._sock.connect((hostname,port))
151  except:
152  raise ControlPortException("unable to connect to %s:%d" % (hostname,port),True)
153 
154  self._eventMap = {}
155  self._responseMap = {}
156  self._sequence = 0
157  self._lock = threading.Lock()
158  self._read,self._write = os.pipe()
159  self._thread = threading.Thread(target=self._run)
160  self._thread.start()
161 
162  def start(self):
163  self._thread.start()
164 
165  def stop(self):
166  os.write(self._write, str.encode("\n") if sys.version_info >= (3,0) else "\n")
167  self._thread.join()
168  self._sock.close()
169 
170  def getManifest(self):
171  request = remotecontrolportapi_pb2.Request()
172  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_QUERY
173  request.query.type = remotecontrolportapi_pb2.TYPE_QUERY_MANIFEST
174 
175  response = self._sendMessage(request)
176 
177  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_QUERY:
178  if response.query.type == remotecontrolportapi_pb2.TYPE_QUERY_MANIFEST:
179  manifest = {}
180  for nem in response.query.manifest.nems:
181  layers = []
182  for component in nem.components:
183  if component.type == remotecontrolportapi_pb2.Response.Query.Manifest.NEM.Component.TYPE_COMPONENT_PHY:
184  name = 'PHY'
185  elif component.type == remotecontrolportapi_pb2.Response.Query.Manifest.NEM.Component.TYPE_COMPONENT_MAC:
186  name = 'MAC'
187  elif component.type == remotecontrolportapi_pb2.Response.Query.Manifest.NEM.Component.TYPE_COMPONENT_SHIM:
188  name = 'SHIM'
189  else:
190  name = 'TRANSPORT'
191 
192  layers.append((component.buildId,name,component.plugin))
193 
194  manifest[nem.id] = tuple(layers)
195 
196  return manifest
197  else:
198  raise ControlPortException('malformed manifest query response')
199  else:
200  raise ControlPortException('malformed query response')
201 
202 
203  def getConfiguration(self,buildId,parameters = ()):
204  request = remotecontrolportapi_pb2.Request()
205  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_QUERY
206  request.query.type = remotecontrolportapi_pb2.TYPE_QUERY_CONFIGURATION
207  request.query.configuration.buildId = buildId
208  for parameter in parameters:
209  request.query.configuration.names.append(parameter)
210 
211  response = self._sendMessage(request)
212 
213  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_QUERY:
214  if response.query.type == remotecontrolportapi_pb2.TYPE_QUERY_CONFIGURATION:
215  configuration = {}
216  for parameter in response.query.configuration.parameters:
217  values = []
218 
219  for value in parameter.values:
220  values.append(fromAny(value))
221 
222  configuration[parameter.name] = tuple(values)
223 
224  return configuration
225  else:
226  raise ControlPortException('malformed configuration query response')
227  else:
228  raise ControlPortException('malformed query response')
229 
230 
231  def getStatistic(self,buildId,elements = ()):
232  request = remotecontrolportapi_pb2.Request()
233  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_QUERY
234  request.query.type = remotecontrolportapi_pb2.TYPE_QUERY_STATISTIC
235  request.query.statistic.buildId = buildId
236  for element in elements:
237  request.query.statistic.names.append(element)
238 
239  response = self._sendMessage(request)
240 
241  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_QUERY:
242  if response.query.type == remotecontrolportapi_pb2.TYPE_QUERY_STATISTIC:
243  statistics = {}
244  for element in response.query.statistic.elements:
245  statistics[element.name] = fromAny(element.value)
246 
247  return statistics
248 
249  else:
250  raise ControlPortException('malformed statistic query response')
251  else:
252  raise ControlPortException('malformed query response')
253 
254 
255  def getStatisticTable(self,buildId,tables = ()):
256  request = remotecontrolportapi_pb2.Request()
257  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_QUERY
258  request.query.type = remotecontrolportapi_pb2.TYPE_QUERY_STATISTICTABLE
259  request.query.statisticTable.buildId = buildId
260  for table in tables:
261  request.query.statisticTable.names.append(table)
262 
263  response = self._sendMessage(request)
264 
265  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_QUERY:
266  if response.query.type == remotecontrolportapi_pb2.TYPE_QUERY_STATISTICTABLE:
267  tables = {}
268 
269  for table in response.query.statisticTable.tables:
270  tableData = []
271  for row in table.rows:
272  rowData = []
273  for value in row.values:
274  rowData.append(fromAny(value))
275  tableData.append(tuple(rowData))
276  tables[table.name] = (tuple(table.labels),tuple(tableData))
277 
278  return tables
279  else:
280  raise ControlPortException('malformed statisticTable query response')
281  else:
282  raise ControlPortException('malformed query response')
283 
284 
285  def clearStatistic(self,buildId,elements = ()):
286  request = remotecontrolportapi_pb2.Request()
287  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_UPDATE
288  request.update.type = remotecontrolportapi_pb2.TYPE_UPDATE_STATISTICCLEAR
289  request.update.statisticClear.buildId = buildId
290  for element in elements:
291  request.update.statisticClear.names.append(element)
292 
293  response = self._sendMessage(request)
294 
295  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_UPDATE:
296  if response.update.type == remotecontrolportapi_pb2.TYPE_UPDATE_STATISTICCLEAR:
297  return
298  else:
299  raise ControlPortException('malformed statistic clear update response')
300  else:
301  raise ControlPortException('malformed update response')
302 
303 
304  def clearTable(self,buildId,tables = ()):
305  request = remotecontrolportapi_pb2.Request()
306  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_UPDATE
307  request.update.type = remotecontrolportapi_pb2.TYPE_UPDATE_STATISTICTABLECLEAR
308  request.update.statisticTableClear.buildId = buildId
309  for table in tables:
310  request.update.statisticTableClear.names.append(table)
311 
312  response = self._sendMessage(request)
313 
314  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_UPDATE:
315  if response.update.type == remotecontrolportapi_pb2.TYPE_UPDATE_STATISTICTABLECLEAR:
316  return
317  else:
318  raise ControlPortException('malformed statistic table clear update response')
319  else:
320  raise ControlPortException('malformed update response')
321 
322 
323  def updateConfiguration(self,buildId,updates):
324  request = remotecontrolportapi_pb2.Request()
325  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_UPDATE
326  request.update.type = remotecontrolportapi_pb2.TYPE_UPDATE_CONFIGURATION
327  request.update.configuration.buildId = buildId
328 
329  for (name,dataType,values) in updates:
330  parameter = request.update.configuration.parameters.add()
331  parameter.name = name
332  for value in values:
333  any = parameter.values.add()
334  toAny(any,value,dataType)
335 
336  response = self._sendMessage(request)
337 
338  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_UPDATE:
339  if response.update.type == remotecontrolportapi_pb2.TYPE_UPDATE_CONFIGURATION:
340  return
341  else:
342  raise ControlPortException('malformed configuration update response')
343  else:
344  raise ControlPortException('malformed update response')
345 
346 
347  def setLogLevel(self,level):
348  request = remotecontrolportapi_pb2.Request()
349  request.type = remotecontrolportapi_pb2.Request.TYPE_REQUEST_UPDATE
350  request.update.type = remotecontrolportapi_pb2.TYPE_UPDATE_LOGLEVEL
351  request.update.logLevel.level = level
352 
353  response = self._sendMessage(request)
354 
355  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_UPDATE:
356  if response.update.type == remotecontrolportapi_pb2.TYPE_UPDATE_LOGLEVEL:
357  return
358  else:
359  raise ControlPortException('malformed log level update response')
360  else:
361  raise ControlPortException('malformed update response')
362 
363 
364 
365  def _sendMessage(self,request):
366  self._lock.acquire()
367 
368  if not self._connected:
369  self._lock.release()
370  raise ControlPortException('connection terminated by server',True)
371 
372  sequence = ++self._sequence
373 
374  request.sequence = sequence
375 
376  msg = request.SerializeToString()
377 
378  self._sock.send(struct.pack("!L%ds" % len(msg),len(msg),msg))
379 
380  event = threading.Event()
381 
382  self._eventMap[sequence] = (event)
383 
384  self._lock.release()
385 
386  event.wait()
387 
388  self._lock.acquire()
389 
390  if self._connected:
391  response = self._responseMap[sequence]
392 
393  del self._responseMap[sequence]
394 
395  del self._eventMap[sequence]
396 
397  self._lock.release()
398 
399  if response.type == remotecontrolportapi_pb2.Response.TYPE_RESPONSE_ERROR:
400  raise ControlPortException(response.error.description);
401 
402  return response
403  else:
404  self._lock.release()
405  raise ControlPortException('connection terminated by server',True)
406 
407 
408  def _run(self):
409  buffer = bytes() if sys.version_info >= (3,0) else ""
410  messageLengthBytes = 0
411  running = True
412 
413  while running:
414  readable,_,_ = select.select([self._sock,self._read],[],[])
415 
416  for fd in readable:
417  if fd is self._sock:
418  if not messageLengthBytes:
419  data = self._sock.recv(4-len(buffer))
420 
421  if not len(data):
422  running = False
423  break
424 
425  buffer+=data
426 
427  if(len(buffer) == 4):
428  (messageLengthBytes,) = struct.unpack('!I',buffer);
429  buffer = bytes() if sys.version_info >= (3,0) else ""
430 
431  else:
432  data = self._sock.recv(messageLengthBytes-len(buffer))
433 
434  if not len(data):
435  running = False
436  break
437 
438  buffer+=data
439 
440  if(len(buffer) == messageLengthBytes):
441  response = remotecontrolportapi_pb2.Response()
442 
443  response.ParseFromString(buffer)
444 
445  self._lock.acquire()
446 
447  if response.reference in self._eventMap:
448  self._responseMap[response.reference] = response
449  self._eventMap[response.reference].set()
450 
451  self._lock.release()
452 
453  messageLengthBytes = 0
454  buffer = bytes() if sys.version_info >= (3,0) else ""
455 
456  elif fd is self._read:
457  running = False
458  break
459 
460  self._lock.acquire()
461 
462  self._connected = False
463 
464  for event in list(self._eventMap.values()):
465  event.set()
466 
467  self._lock.release()
468 
469  if self._disconnect:
470  self._disconnect()
struct EtherAddrBytes bytes
Definition: netutils.h:390