|
1 | | -""" |
2 | | - dsc_datatool |
3 | | - ~~~~~~~~~~~~ |
4 | | -
|
5 | | - DSC datatool |
6 | | -
|
7 | | - :copyright: 2020 OARC, Inc. |
| 1 | +"""dsc_datatool |
| 2 | +
|
| 3 | +The main Python module for the command line tool `dsc-datatool`, see |
| 4 | +`man dsc-datatool` on how to run it. |
| 5 | +
|
| 6 | +On runtime it will load all plugins under the following module path: |
| 7 | +- dsc_datatool.input |
| 8 | +- dsc_datatool.output |
| 9 | +- dsc_datatool.generator |
| 10 | +- dsc_datatool.transformer |
| 11 | +
|
| 12 | +Each plugin category should base it class on one of the follow superclasses: |
| 13 | +- dsc_datatool.Input |
| 14 | +- dsc_datatool.Output |
| 15 | +- dsc_datatool.Generator |
| 16 | +- dsc_datatool.Transformer |
| 17 | +
|
| 18 | +Doing so it will be automatically registered as available and indexed in |
| 19 | +the following public dicts using the class name: |
| 20 | +- inputs |
| 21 | +- outputs |
| 22 | +- generators |
| 23 | +- transformers |
| 24 | +
|
| 25 | +Example of an output: |
| 26 | +
|
| 27 | + from dsc_datatool import Output |
| 28 | + class ExampleOutput(Output): |
| 29 | + def process(self, datasets) |
| 30 | + ... |
| 31 | +
|
| 32 | +:copyright: 2020 OARC, Inc. |
8 | 33 | """ |
9 | 34 |
|
10 | 35 | __version__ = '1.0.0' |
|
27 | 52 |
|
28 | 53 |
|
29 | 54 | class Dataset(object): |
| 55 | + """A representation of a DSC dataset |
| 56 | +
|
| 57 | + A DSC dataset is one to two dimensional structure where the last |
| 58 | + dimension holds an array of values and counters. |
| 59 | +
|
| 60 | + It is based on the XML structure of DSC: |
| 61 | +
|
| 62 | + <array name="pcap_stats" dimensions="2" start_time="1563520560" stop_time="1563520620"> |
| 63 | + <dimension number="1" type="ifname"/> |
| 64 | + <dimension number="2" type="pcap_stat"/> |
| 65 | + <data> |
| 66 | + <ifname val="eth0"> |
| 67 | + <pcap_stat val="filter_received" count="5625"/> |
| 68 | + <pcap_stat val="pkts_captured" count="4894"/> |
| 69 | + <pcap_stat val="kernel_dropped" count="731"/> |
| 70 | + </ifname> |
| 71 | + </data> |
| 72 | + </array> |
| 73 | +
|
| 74 | + Attributes: |
| 75 | + - name: The name of the dataset |
| 76 | + - start_time: The start time of the dataset in seconds |
| 77 | + - stop_time: The stop time of the dataset in seconds |
| 78 | + - dimensions: An array with `Dimension`, the first dimension |
| 79 | + """ |
30 | 80 | name = None |
31 | 81 | start_time = None |
32 | 82 | stop_time = None |
33 | 83 | dimensions = None |
34 | 84 |
|
| 85 | + |
35 | 86 | def __init__(self): |
36 | 87 | self.dimensions = [] |
37 | 88 |
|
| 89 | + |
38 | 90 | def __repr__(self): |
39 | 91 | return '<Dataset name=%r dimension=%r>' % (self.name, self.dimensions) |
40 | 92 |
|
41 | 93 |
|
42 | 94 | class Dimension(object): |
| 95 | + """A representation of a DSC dimension |
| 96 | +
|
| 97 | + A DSC dataset dimension which can be the first or second dimension, |
| 98 | + see `Dataset` for more information. |
| 99 | +
|
| 100 | + Attributes: |
| 101 | + - name: The name of the dimension |
| 102 | + - value: Is set to the value of the dimension if it's the first dimension |
| 103 | + - values: A dict of values with corresponding counters if it's the second dimension |
| 104 | + """ |
43 | 105 | name = None |
44 | 106 | value = None |
45 | 107 | values = None |
46 | 108 | dimensions = None |
47 | 109 |
|
| 110 | + |
48 | 111 | def __init__(self, name): |
49 | 112 | self.name = name |
50 | 113 | self.values = {} |
51 | 114 | self.dimensions = [] |
52 | 115 |
|
| 116 | + |
53 | 117 | def __repr__(self): |
54 | 118 | return '<Dimension name=%r value=%r dimension=%r>' % (self.name, self.values or self.value, self.dimensions) |
55 | 119 |
|
56 | 120 |
|
57 | 121 | class Input(object): |
| 122 | + """Base class of an input plugin""" |
| 123 | + |
| 124 | + |
58 | 125 | def process(self, file): |
| 126 | + """Input.process(...) -> [ Dataset, ... ] |
| 127 | +
|
| 128 | + Called to process a file and return an array of `Dataset`'s found in it. |
| 129 | + """ |
59 | 130 | raise Exception('process() not overloaded') |
60 | 131 |
|
| 132 | + |
61 | 133 | def __init_subclass__(cls): |
| 134 | + """This method is called when a class is subclassed and it will |
| 135 | + register the input plugin in `inputs`.""" |
62 | 136 | global inputs |
63 | 137 | if cls.__name__ in inputs: |
64 | 138 | raise Exception('Duplicate input module: %s already exists' % cls.__name__) |
65 | 139 | inputs[cls.__name__] = cls |
66 | 140 |
|
67 | 141 |
|
68 | 142 | class Output(object): |
| 143 | + """Base class of an output plugin""" |
| 144 | + |
| 145 | + |
69 | 146 | def process(self, datasets): |
| 147 | + """Output.process([ Dataset, ... ]) |
| 148 | +
|
| 149 | + Called to output the `Dataset`'s in the given array.""" |
70 | 150 | raise Exception('process() not overloaded') |
71 | 151 |
|
| 152 | + |
72 | 153 | def __init__(self, opts): |
| 154 | + """instance = Output({ 'opt': value, ... }) |
| 155 | +
|
| 156 | + Called to create an instance of the output plugin, will get a dict |
| 157 | + with options provided on command line.""" |
73 | 158 | pass |
74 | 159 |
|
| 160 | + |
75 | 161 | def __init_subclass__(cls): |
| 162 | + """This method is called when a class is subclassed and it will |
| 163 | + register the output plugin in `outputs`.""" |
76 | 164 | global outputs |
77 | 165 | if cls.__name__ in outputs: |
78 | 166 | raise Exception('Duplicate output module: %s already exists' % cls.__name__) |
79 | 167 | outputs[cls.__name__] = cls |
80 | 168 |
|
81 | 169 |
|
82 | 170 | class Generator(object): |
| 171 | + """Base class of a generator plugin""" |
| 172 | + |
| 173 | + |
83 | 174 | def process(self, datasets): |
| 175 | + """Generator.process([ Dataset, ... ]) -> [ Dataset, ... ] |
| 176 | +
|
| 177 | + Called to generate additional `Dataset`'s based on the given array |
| 178 | + of `Dataset`'s.""" |
84 | 179 | raise Exception('process() not overloaded') |
85 | 180 |
|
| 181 | + |
86 | 182 | def __init__(self, opts): |
| 183 | + """instance = Generator({ 'opt': value, ... }) |
| 184 | +
|
| 185 | + Called to create an instance of the generator plugin, will get a dict |
| 186 | + with options provided on command line.""" |
87 | 187 | pass |
88 | 188 |
|
| 189 | + |
89 | 190 | def __init_subclass__(cls): |
| 191 | + """This method is called when a class is subclassed and it will |
| 192 | + register the generator plugin in `generators`.""" |
90 | 193 | global generators |
91 | 194 | if cls.__name__ in generators: |
92 | 195 | raise Exception('Duplicate generator module: %s already exists' % cls.__name__) |
93 | 196 | generators[cls.__name__] = cls |
94 | 197 |
|
95 | 198 |
|
96 | 199 | class Transformer(object): |
| 200 | + """Base class of a transformer plugin""" |
| 201 | + |
| 202 | + |
97 | 203 | def process(self, datasets): |
| 204 | + """Transformer.process([ Dataset, ... ]) |
| 205 | +
|
| 206 | + Called to do transformation of the given `Dataset`'s, as in modifying |
| 207 | + them directly.""" |
98 | 208 | raise Exception('process() not overloaded') |
99 | 209 |
|
| 210 | + |
100 | 211 | def __init__(self, opts): |
| 212 | + """instance = Transformer({ 'opt': value, ... }) |
| 213 | +
|
| 214 | + Called to create an instance of the transformer plugin, will get a dict |
| 215 | + with options provided on command line.""" |
101 | 216 | pass |
102 | 217 |
|
| 218 | + |
103 | 219 | def __init_subclass__(cls): |
| 220 | + """This method is called when a class is subclassed and it will |
| 221 | + register the transformer plugin in `transformers`.""" |
104 | 222 | global transformers |
105 | 223 | if cls.__name__ in transformers: |
106 | 224 | raise Exception('Duplicate transformer module: %s already exists' % cls.__name__) |
107 | 225 | transformers[cls.__name__] = cls |
108 | 226 |
|
109 | 227 |
|
110 | 228 | def main(): |
| 229 | + """Called when running `dsc-datatool`.""" |
111 | 230 | def iter_namespace(ns_pkg): |
112 | 231 | return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + ".") |
113 | 232 |
|
|
0 commit comments