-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharcconf_getconfig.py
256 lines (226 loc) · 7.29 KB
/
arcconf_getconfig.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import re
__version__ = '0.1.aplha'
__all__ = ['ArcconfParser', 'ArcconfGetconfig', ]
def _list2dict(lst):
name = lst[0] if isinstance(lst, list) else None
dct = {}
for kv in (lst[1:] if name is not None else lst):
if isinstance(kv, tuple):
dct[kv[0]] = kv[1]
else:
_n, _d = _list2dict(kv)
dct[_n] = _d
return name, dct
class ArcconfParser(object):
def __init__(self):
self.__result = []
self._last_spaces = -1
self._stack = [{
'spaces': self._last_spaces,
'branch': self.__result,
}]
# _stack - стек элементов, в которые добавляются новые записи
self._wasdash = False
@staticmethod
def is_multidash(line):
return re.compile('^ *-').match(line)
@staticmethod
def lspace_count(line):
'''return the number of spaces at begining of the line'''
return re.compile('^( *)[^ ].*$').match(line).groups()[0].__len__()
@staticmethod
def get_k_v(line):
_x = line.split(': ', 1)
if _x.__len__() < 2:
return
return tuple([s.strip() for s in _x])
def _crop_stack(self, i, leave_first=False):
_new_stack = []
for _x in self._stack:
if _x['spaces'] < i:
_new_stack.append(_x)
elif _x['spaces'] == i and leave_first:
_new_stack.append(_x)
break
else:
break
self._stack = _new_stack
def _append_branch(self, spaces, branch):
self._stack[-1]['branch'].append(branch)
self._stack.append({
'spaces': spaces,
'branch': branch,
})
def _append_leaf(self, kv):
self._stack[-1]['branch'].append(kv)
def append(self, line):
if not line:
return
_spaces = self.lspace_count(line)
if self.is_multidash(line):
self._wasdash = not self._wasdash
self._last_spaces = _spaces
return
kv = self.get_k_v(line)
if self._wasdash:
# начинается заголовок нового списка элементов
self._crop_stack(_spaces)
self._append_branch(_spaces, [line.strip()])
self._last_spaces = _spaces
return
if kv is None and _spaces == 0:
# text like a ...
# Logical device number \d
self._crop_stack(_spaces, leave_first=(True))
self._append_branch(_spaces, [line.strip()])
self._last_spaces = _spaces
return
if kv is None:
if _spaces < self._last_spaces:
self._crop_stack(_spaces)
self._append_branch(_spaces, [line.strip()])
self._last_spaces = _spaces
else:
self._append_leaf((line.strip(), True))
self._last_spaces = _spaces
else:
self._append_leaf(kv)
return
def get_result(self):
return self.__result
def get_as_dict(self):
return _list2dict(['plug'] + self.__result)[1]
class ArcconfGetconfig(object):
u'''
Parses the output of the command
arcconf getconfig <ID>
'''
def __init__(self, filename=None, raw=None, id=None):
self.id = 1 if id is None else id
if filename is None and id is None:
if os.isatty(sys.stdin.fileno()):
self.id = '1'
self.content = self._get_output(self.id)
else:
self.content = sys.stdin.read()
pass
elif filename:
if filename == '-':
self.content = sys.stdin.read()
else:
self.content = open(filename, 'rt').read()
else:
self.id = id
self.content = self._get_output(self.id)
#
self._result = self.parse_config(self.content)
@staticmethod
def _get_output(id):
from subprocess import Popen, PIPE
try:
from subprocess import DEVNULL
except ImportError:
DEVNULL = open('/dev/null', 'w')
_cmdline = 'arcconf getconfig %s' % id
_env = os.environ.copy()
_env.update(LC_ALL='C', LANG='C', PATH=':'.join((
os.environ.get('PATH', ':'),
'/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin',
)))
_cmd = Popen(
_cmdline,
stdin=DEVNULL,
stdout=PIPE,
stderr=PIPE,
shell=True,
env=_env,
)
_x = _cmd.communicate()
if _cmd.returncode != 0:
raise OSError({
'command': _cmdline,
'returncode': _cmd.returncode,
'stdout': _x[0],
'stderr': _x[1],
})
else:
return _x[0].decode()
@staticmethod
def is_multidash(line):
return re.compile('^ *-').match(line)
@staticmethod
def lspace_count(line):
return re.compile('^( *)[^ ].*$').match(line).groups()[0].__len__()
@staticmethod
def get_k_v(line):
_m = re.compile('^ *([^ :][^:]+[^ :]) +: +([^ :][^:]+[^ :]) *$')
_x = _m.match(line)
return _x.groups() if _x else None
@staticmethod
def parse_config(content):
_result = ArcconfParser()
for line in content.splitlines():
_result.append(line)
return _result
def out(self, jsn, dct):
if dct is False:
_result = self._result.get_result()
else:
_result = self._result.get_as_dict()
if jsn is False:
from pprint import pprint as out
else:
try:
from simplejson import dumps
except ImportError:
from json import dumps
def out(x):
print(dumps(x, indent=2))
out(_result)
def main(argv):
import optparse
parser = optparse.OptionParser(
usage='%prog: [id]|--input=FILE'
' --dict|--raw'
' --print|--json'
)
parser.add_option(
'-i', '--input', dest='filename', metavar='FILE',
help='Read arcconf message from file',
)
parser.add_option(
'--dict', action='store_true', dest='dict',
help='get result as dictionary',
)
parser.add_option(
'--raw', action='store_false', dest='dict',
help='get result raw',
)
parser.add_option(
'--print', action='store_false', dest='json',
help='print result as python internal structure',
)
parser.add_option(
'--json', action='store_true', dest='json',
help='print resula as json',
)
(options, args) = parser.parse_args(argv)
_result = []
if options.filename is not None:
if args:
parser.error('Mutially expusive options <id> and <--input=FILE>')
else:
_result.append(ArcconfGetconfig(filename=options.filename))
elif not args:
_result.append(ArcconfGetconfig())
else:
for i in args:
_result.append(ArcconfGetconfig(id=i))
for i in _result:
i.out(jsn=options.json, dct=options.dict)
if __name__ == '__main__':
main(sys.argv[1:])