35 from __future__
import absolute_import, division, print_function
42 from .
import ControlPortClient
43 from .
import ControlPortException
44 from .
import Manifest
45 from .
import ManifestException
47 class EMANEShell(cmd.Cmd):
49 cmd.Cmd.__init__(self)
50 self.
prompt=
"[emanesh (%s:%d)] ## " % (host,port)
51 self.
_client = ControlPortClient(host,port)
52 self.
_manifest = {
'emulator' : [(0,
'all',
'nemmanager')]}
57 for (nem,components)
in list(self.
_client.getManifest().items()):
61 for component
in components:
62 name = component[1].lower()
64 name += str(shimcount)
68 self.
_mapping[nem][name] = component[0]
69 self.
_manifest[nem].append((component[0],name,component[2]))
71 manifestpath = os.getenv(
'EMANEMANIFESTPATH',
'/usr/share/emane/manifest')
73 for directory
in manifestpath.split(
':'):
74 for manifestXML
in glob.glob(
"%s/*.xml" % directory):
76 manifest = Manifest(manifestXML)
78 except ManifestException:
82 print(
"warning: no plugin manifest XML loaded. Check EMANEMANIFESTPATH.")
91 print(
"error: unknown command:",line.split()[0])
95 The help command is used to get usage information for each 99 <command> ::= 'get' | 'clear' | 'show' | 'info' | 105 The ^D (Ctrl+d) command is used to exit the shell. 118 The exit command is used to exit the shell. 132 The show command is used to display manifest information provided 133 by the connected control port server. 135 The displayed information will include the NEM ids, component layer 136 types present and the name of the component layer plugin. 146 for (nem,components)
in list(self.
_manifest.items()):
147 if nem !=
'emulator':
148 print(
"nem %-3d" % nem, end=
' ')
149 for component
in components:
150 print(
"%s(%s)" % (component[1],component[2]), end=
' ')
153 print(
'error: too many arguements')
157 The info command is used to display information loaded from plugin 158 manifest files. There are four types of info commands: manifest, 159 config, stat and table. The manifest info command is used to display 160 the names of discovered plugins. The config, stat and table info 161 commands are used to display plugin specific item descriptions. 164 The config, stat and table info commands use a single optional 165 plugin-specific name parameter: 166 * config name specifies a configuration parameter name 167 * stat name specifies a statistic element name 168 * table name specifies a table name 170 When no name is specified all known names are listed. 172 usage: info <type> <plugin> [<plugin-specific>] 173 <type> ::= 'config' | 'stat' | 'table' 174 <plugin> ::= plugin-name 175 <plugin-specific> ::= <names> 176 <names> ::= <name> | <name> <names> 177 <name> ::= [A-Za-z0-9]+ 183 ## info config rfpipemaclayer 184 ## info config rfpipemaclayer neighbormetricdeletetime 185 ## info stat emanephy processedEvents 190 print(
'error: missing info type')
193 command = args[0].lower()
195 if command !=
'config' and \
196 command !=
'stat' and \
197 command !=
'table' and \
198 command !=
'manifest':
199 print(
"error: invalid info command type:",args[0])
202 if command ==
"manifest":
205 print(
' Loaded plugin manifests')
215 print(
'error: too many arguements')
222 print(
"error: invalid plugin name or missing manifest:",plugin)
226 if command ==
'config':
230 info = self.
_pluginInfo[plugin].getConfigurationInfo(parameter)
232 for line
in textwrap.wrap(
"Configuration parameter" 233 " information for %s %s" % (plugin,
236 subsequent_indent=
' ',
240 for line
in textwrap.wrap(info[
'description'],
242 subsequent_indent=
' ',
246 print(
' default :',info[
'default'])
247 print(
' required :',info[
'required'])
248 print(
' modifiable:',info[
'modifiable'])
250 if 'numeric' in info:
251 print(
" type :",info[
'numeric'][
'type'])
252 print(
" range : [%s,%s]" % (info[
'numeric'][
'minValue'],
253 info[
'numeric'][
'maxValue']))
255 print(
" type :",info[
'nonnumeric'][
'type'])
257 print(
" regex : %s" % info[
'regex'])
258 print(
" occurs : [%d,%d]" % (info[
'minOccurs'],info[
'maxOccurs']))
259 print(
' default :', end=
' ')
260 for value
in info[
'values']:
261 print(value,
' ', end=
' ')
266 print(
"error: invalid configuration parameter name:",parameter)
269 elif command ==
'stat':
272 info = self.
_pluginInfo[plugin].getStatisticInfo(element)
274 for line
in textwrap.wrap(
"Statistic element information for" 278 subsequent_indent=
' ',
282 for line
in textwrap.wrap(info[
'description'],
284 subsequent_indent=
' ',
288 print(
' clearable :',info[
'clearable'])
289 print(
' type :',info[
'type'])
293 print(
"error: invalid statistic element name:",element)
297 elif command ==
'table':
300 info = self.
_pluginInfo[plugin].getTableInfo(table)
302 for line
in textwrap.wrap(
"Table information for %s %s" % (plugin,
305 subsequent_indent=
' ',
309 for line
in textwrap.wrap(info[
'description'],
311 subsequent_indent=
' ',
315 print(
' clearable :',info[
'clearable'])
319 print(
"error: invalid statistic table name:",table)
324 print(
'error: too many arguements')
328 if command ==
'config':
330 print(
' Available configuration parameters for',plugin)
332 names = self.
_pluginInfo[plugin].getAllConfiguration()
338 elif command ==
'stat':
340 print(
' Available statistic elements for',plugin)
342 names = self.
_pluginInfo[plugin].getAllStatistics()
348 elif command ==
'table':
350 print(
' Available statistic tables for',plugin)
360 print(
"error: missing plugin name")
366 completions = {
'info' : [
'config',
'stat',
'table',
'manifest'],
373 if args[1] ==
'config':
374 completions[args[2]] = self.
_pluginInfo[args[2]].getAllConfiguration()
375 elif args[1] ==
'stat':
376 completions[args[2]] = self.
_pluginInfo[args[2]].getAllStatistics()
377 elif args[1] ==
'table':
378 completions[args[2]] = self.
_pluginInfo[args[2]].getAllTables()
382 item
for item
in completions[args[-2]]
383 if item.startswith(args[-1])
386 return completions[args[-1]]
390 The get command is used to get configuration, statistic and 391 statistic table values. 393 All get types use optional target-specific name parameters: 394 * 'config' names specify configuration parameter names 395 * 'stat' names specify statistic element names 396 * 'table' names specify table names 398 When no names are specified all items are retrieved. 400 usage: get <type> <targets> <layer> [<target-specific>] 401 <type> ::= 'config' | 'stat' | 'table' 402 <targets> ::= <nem> | <nem> <target> | 'emulator' | 404 <layer> ::= 'all' | 'mac' | 'phy' | <shim> 405 <nem> ::= [1-9] | [1-9][0-9]+ 406 <shim> ::= 'shim'[0-9]+ 407 <target-specific> ::= <names> | '' 408 <names> ::= <name> | <name> <names> 409 <name> ::= [A-Za-z0-9]+ 413 Get all configuration info from all NEM layers and the emulator 416 Get all statistic tables from all NEM mac layers 417 ## get table nems mac 419 Get two statistic items from the mac layers of NEM 1 and 2 420 ## get stat 1 2 mac processedEvents processedDownstreamControl 426 text, line, begidx, endidx,
"",
427 config=
'getAllConfiguration',
428 stat=
'getAllStatistics',
429 table=
'getAllTables')
433 The clear command is used to clear statistic elements that have 434 been designated as clearable. 436 Optional target-specific statistic element names can be specified. 437 When no names are specified all clearable statistic elements will 440 usage: clear stat <targets> <layer> [<target-specific>] 441 <type> ::= 'stat' | 'table' 442 <targets> ::= <nem> | <nem> <target> | 'emulator' | 444 <layer> ::= 'all' | 'mac' | 'phy' | <shim> 445 <nem> ::= [1-9] | [1-9][0-9]+ 446 <shim> ::= 'shim'[0-9]+ 447 <target-specific> ::= <names> | '' 448 <names> ::= <name> | <name> <names> 449 <name> ::= [A-Za-z0-9]+ 452 Clear all statistics from all NEM layers and the emulator 455 Clear all statistics from all NEM mac layers 456 ## clear stat nems mac 458 Clear two statistic items from the phy layers of NEM 1 and 2 459 ## clear stat 1 2 phy processedEvents processedDownstreamControl 466 text, line, begidx, endidx,
"",
467 stat=
'getClearableStatistics',
468 table=
'getClearableTables')
470 def _process(self,action,args):
475 command = args[0].lower()
476 if command ==
'config':
477 command =
'configuration' 478 elif command ==
'stat':
479 command =
'statistic' 480 elif command ==
'table':
483 print(
"error: invalid get command type:",args[0])
486 print(
"error: missing get command type")
489 elif(action ==
'clear'):
491 command = args[0].lower()
492 if command ==
'stat':
493 command =
'statistic' 494 elif command ==
'table':
497 print(
"error: invalid clear command type:",args[0])
500 print(
"error: missing clear command type")
506 if len(args) > index:
507 for arg
in args[index:]:
511 print(
"error: invalid target:",nem)
516 if arg.lower() ==
'nems':
517 targets.extend([x
for x
in list(self.
_manifest.keys())
if x !=
'emulator'])
519 elif arg.lower() ==
'*':
520 targets.extend(list(self.
_manifest.keys()))
522 elif arg.lower() ==
'emu' or arg.lower() ==
'emulator':
523 targets.append(
'emulator')
530 print(
"error: missing target(s)")
533 targets = list(set(targets))
537 if len(args) > index:
538 component = args[index].lower()
539 if component !=
'phy' and \
540 component !=
'mac' and \
541 component !=
'transport' and \
542 component !=
'all' and \
543 not (re.match(
'^shim\d+$', component)
and component
in self.
_shims):
544 print(
"error: invalid component layer:",args[index])
549 print(
"error: missing component layer")
553 if component !=
"all":
554 for target
in set(targets):
555 if component
not in self.
_mapping[target]:
556 if target !=
'emulator':
557 print(
"error: component not present in target %d: %s" % (target,component))
559 print(
"error: component not present in emulator: %s" % (component))
564 if len(args) > index:
567 for target
in targets:
568 for componentInfo
in self.
_manifest[target]:
569 if component ==
"all" or component == componentInfo[1]:
571 if command ==
"configuration":
573 entries = self.
_client.getConfiguration(componentInfo[0],names)
575 for name
in sorted(entries.keys()):
576 values = entries[name]
577 if target !=
'emulator':
578 print(
"nem %-3d %s "%(target,componentInfo[1]),name,
"=",
",".join([str(x[0])
for x
in values]))
580 print(
"emulator",name,
"=",
",".join([str(x[0])
for x
in values]))
582 except ControlPortException
as exp:
586 if target !=
'emulator':
587 print(
"nem %-3d %s "%(target,componentInfo[1]),exp)
589 print(
"emulator",exp)
592 elif command ==
"statistic":
594 statistics = self.
_client.getStatistic(componentInfo[0],names)
596 for name
in sorted(statistics.keys()):
597 value = statistics[name]
598 if target !=
'emulator':
599 print(
"nem %-3d %s "%(target,componentInfo[1]),name,
"=",value[0])
601 print(
"emulator",name,
"=",value[0])
603 except ControlPortException
as exp:
607 if target !=
'emulator':
608 print(
"nem %-3d %s "%(target,componentInfo[1]),exp)
610 print(
"emulator",exp)
613 elif command ==
"table":
615 statisticTables = self.
_client.getStatisticTable(componentInfo[0],names)
617 for name
in sorted(statisticTables.keys()):
619 (labels,rows) = statisticTables[name]
622 widths.append(len(label))
627 widths[i] = max(len(str(item[0])),widths[i])
630 if target !=
'emulator':
631 print(
"nem %-3d %s %s"%(target,componentInfo[1],name))
633 print(
"emulator", name)
637 print(
'|',str(label).ljust(widths[i]), end=
' ')
646 print(
'|',str(item[0]).ljust(widths[i]), end=
' ')
650 except ControlPortException
as exp:
654 if target !=
'emulator':
655 print(
"nem %-3d %s "%(target,componentInfo[1]),exp)
657 print(
"emulator",exp)
659 elif action ==
"clear":
660 if command ==
"statistic":
662 self.
_client.clearStatistic(componentInfo[0],names)
664 if target !=
'emulator':
665 print(
"nem %-3d %s "%(target,componentInfo[1]),
"statistics cleared")
667 print(
"emulator statistics cleared")
669 except ControlPortException
as exp:
673 if target !=
'emulator':
674 print(
"nem %-3d %s "%(target,componentInfo[1]),exp)
676 print(
"emulator",exp)
678 elif command ==
"table":
680 self.
_client.clearTable(componentInfo[0],names)
682 if target !=
'emulator':
683 print(
"nem %-3d %s "%(target,componentInfo[1]),
"tables cleared")
685 print(
"emulator tables cleared")
687 except ControlPortException
as exp:
691 if target !=
'emulator':
692 print(
"nem %-3d %s "%(target,componentInfo[1]),exp)
694 print(
"emulator",exp)
699 text, line, begidx, endidx,
"=",
700 config=
'getModifiableConfiguration')
705 The set command is used to set configuration elements that have 706 been designated as modifiable. 708 One or more configuration parameter value expressions can be 711 usage: set config <targets> <layer> <expressions> 712 <targets> ::= <nem> | <nem> <target> | 'nems' | 714 <layer> ::= 'all' | 'mac' | 'phy' | <shim> 715 <nem> ::= [1-9] | [1-9][0-9]+ 716 <shim> ::= 'shim'[0-9]+ 717 <nem> ::= [1-9] | [1-9][0-9]+ 718 <expressions> ::= <expression> | <expression> <expressions> 719 <expression> ::= <name>'='<values> 720 <name> ::= [.A-Za-z0-9]+ 721 <values> ::= <value> | <value>','<values> 722 <value> ::= value-string 725 Set the txpower parameter for all NEM phy layers 726 ## set config nems phy txpower=20 728 Set the cwmin0 and cwmax0 parameters for NEM 1, 2 and 3 mac layers 729 ## set config 1 2 3 mac cwmin0=100 cwmax0=200 734 command = args[0].lower()
735 if command ==
'config':
736 command =
'configuration' 738 print(
"error: invalid get command type:",args[0])
741 print(
"error: missing set command type")
748 for arg
in args[index:]:
750 nem = int(args[index])
753 print(
"error: invalid target:",target)
759 if args[index] ==
'nems':
760 targets = [x
for x
in list(self.
_mapping.keys())
if x !=
'emulator']
766 print(
"error: missing target")
771 if len(args) > index:
772 component = args[index].lower()
773 if component !=
'phy' and \
774 component !=
'mac' and \
775 component !=
'transport' and \
776 component !=
'all' and \
777 not (re.match(
'^shim\d+$', component)
and component
in self.
_shims):
778 print(
"error: invalid component layer:",args[index])
783 print(
"error: missing component layer")
786 for target
in targets:
787 if component !=
'all' and component
not in self.
_mapping[target]:
788 print(
"error: component not present in target %d: %s" % (target,component))
793 for target
in targets:
795 if len(args) > index:
796 for expression
in args[index:]:
797 m = re.match(
'^([.0-9A-Za-z]+)=(.+)', expression)
802 if val
in (
'yes',
'on',
'enable',
'true',
'1'):
804 elif val
in (
'no',
'off',
'disable',
'false',
'0'):
809 convert = {
'uint64' : (ControlPortClient.TYPE_UINT64,int),
810 'uint32' : (ControlPortClient.TYPE_UINT32,int),
811 'uint16' : (ControlPortClient.TYPE_UINT16,int),
812 'uint8' : (ControlPortClient.TYPE_UINT8,int),
813 'int64' : (ControlPortClient.TYPE_INT64,int),
814 'int32' : (ControlPortClient.TYPE_INT32,int),
815 'int16' : (ControlPortClient.TYPE_INT16,int),
816 'int8' : (ControlPortClient.TYPE_INT8,int),
817 'bool' : (ControlPortClient.TYPE_BOOLEAN,toBool),
818 'string': (ControlPortClient.TYPE_STRING,str),
819 'inetaddr' : (ControlPortClient.TYPE_INETADDR,str),
820 'float' : (ControlPortClient.TYPE_FLOAT,float),
821 'double' : (ControlPortClient.TYPE_DOUBLE,float)}
825 items = m.group(2).split(
',')
827 for (_,layer,plugin)
in self.
_manifest[target]:
828 if component ==
'all' or layer == component:
830 info = self.
_pluginInfo[plugin].getConfigurationInfo(name)
832 if 'numeric' in info:
833 dataType = info[
'numeric'][
'type']
836 dataType = info[
'nonnumeric'][
'type']
839 print(
"error: nem %hu %s unknown configuration paramater: %s" % (target,
848 values.append(convert[dataType][1](item))
851 print(
"error: invalid conversion %s type %s : %s" % (name,dataType,item))
854 updates.append((name,convert[dataType][0],tuple(values)))
857 print(
"error: invalid configuration parameter format:", expression)
860 print(
"error: missing configration items")
865 buildId = self.
_mapping[target][component]
868 self.
_client.updateConfiguration(buildId,updates)
869 print(
"nem %-3d %s "%(target,component),
"configuration updated")
871 except ControlPortException
as exp:
875 print(
"nem %-3d %s "%(target,component),exp)
878 def _completeMany(self,action,text, line, begidx, endidx,trailer,**subactions):
886 completions = {action : list(subactions.keys())}
890 completions = {action : list(subactions.keys())}
892 if args[1]
in subactions:
894 completions[subaction] = [str(x)
for x
in list(self.
_mapping.keys())]
895 completions[subaction].extend([
'*',
'nems'])
897 elif len(args) > 2
and args[1]
in subactions:
907 nems |= set([x
for x
in list(self.
_mapping.keys())
if x !=
'emulator'])
912 elif arg ==
'emulator':
915 elif arg ==
'phy' or \
917 arg ==
'transport' or \
919 (re.match(
'^shim\d+$', arg)
and arg
in self.
_shims):
926 layers = set(self.
_mapping[nem].keys())
928 layers = layers & set(self.
_mapping[nem].keys())
933 completions[
'nems'] = list(layers)
934 completions[
'*'] = [
'all']
939 possibilities = [x
for x
in list(self.
_mapping.keys())
if x !=
'emulator']
942 if not (
'nems' in args
and 'emulator' in args):
943 possibilities.extend([
'*'])
945 if 'nems' not in args:
947 possibilities.extend([
'nems'])
949 if 'emulator' not in args:
951 possibilities.extend([
'emulator'])
953 remaining = list(set(possibilities) - nems)
960 if args[index]
not in layers
and args[index] !=
'*':
961 completions[args[index]] = [str(x)
for x
in remaining]
962 completions[args[index]].extend(list(layers))
968 if layer ==
'all' or l == layer:
969 method = getattr(self.
_pluginInfo[plugin],subactions[args[1]])
970 items = [
"".join([x,trailer])
for x
in method()]
974 params = params & set(items)
977 completions[layer] = list(params)
982 if text
or line[-1] !=
' ':
985 item
for item
in params
986 if item.startswith(args[-1])
990 item
for item
in completions[args[-2]]
991 if item.startswith(args[-1])
997 return completions[args[-1]]
999 except Exception
as err:
1004 completions = {
'loglevel' : [
'0',
'1',
'2',
'3',
'4']}
1008 item
for item
in completions[args[-2]]
1009 if item.startswith(args[-1])
1012 return completions[args[-1]]
1016 The loglevel command is used to set the emulator loglevel. 1019 1 - Abort log messages 1020 2 - Error log messages 1021 3 - Info log messages 1022 4 - Debug log messages 1024 usage: loglevel [0,4] 1032 print(
'error: invalid number of arguments')
1036 self.
_client.setLogLevel(int(args[0]))
1037 print(
"log level updated")
1039 except ControlPortException
as exp:
1043 print(
"error: ",exp)
1045 print(
"error: invalid log level")
def do_exit(self, message)
def complete_loglevel(self, text, line, begidx, endidx)
def _completeMany(self, action, text, line, begidx, endidx, trailer, subactions)
def __init__(self, host, port)
def complete_set(self, text, line, begidx, endidx)
def _process(self, action, args)
def do_loglevel(self, args)
def complete_clear(self, text, line, begidx, endidx)
def complete_info(self, text, line, begidx, endidx)
def complete_get(self, text, line, begidx, endidx)