]>
Commit | Line | Data |
---|---|---|
a6f80f0e | 1 | #!/usr/bin/python |
3e8ee54f | 2 | # |
3 | # Copyright 2013. Cumulus Networks, Inc. | |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com | |
5 | # | |
6 | # networkInterfaces -- | |
7 | # ifupdown network interfaces file parser | |
8 | # | |
a6f80f0e | 9 | |
10 | import collections | |
a6f80f0e | 11 | import logging |
12 | import glob | |
739f665b | 13 | import re |
3e8ee54f | 14 | from iface import * |
a6f80f0e | 15 | |
16 | class networkInterfaces(): | |
17 | ||
18 | hotplugs = {} | |
19 | auto_ifaces = [] | |
20 | callbacks = {} | |
21 | ||
22 | ifaces_file = "/etc/network/interfaces" | |
23 | ||
24 | def __init__(self): | |
25 | self.logger = logging.getLogger('ifupdown.' + | |
26 | self.__class__.__name__) | |
27 | self.callbacks = {'iface_found' : None} | |
28 | self.allow_classes = {} | |
29 | ||
30 | ||
31 | def subscribe(self, callback_name, callback_func): | |
32 | if callback_name not in self.callbacks.keys(): | |
33 | print 'warning: invalid callback ' + callback_name | |
34 | return -1 | |
35 | ||
36 | self.callbacks[callback_name] = callback_func | |
37 | ||
38 | ||
39 | def ignore_line(self, line): | |
40 | l = line.strip('\n ') | |
41 | ||
42 | if len(l) == 0 or l[0] == '#': | |
43 | return 1 | |
44 | ||
45 | return 0 | |
46 | ||
47 | def process_allow(self, lines, cur_idx, lineno): | |
3e8ee54f | 48 | allow_line = lines[cur_idx] |
a6f80f0e | 49 | |
50 | words = allow_line.split() | |
51 | if len(words) <= 1: | |
52 | raise Exception('invalid allow line \'%s\'' %allow_line) | |
53 | ||
54 | allow_class = words[0].split('-')[1] | |
55 | ifacenames = words[1:] | |
56 | ||
57 | if self.allow_classes.get(allow_class) is not None: | |
58 | for i in ifacenames: | |
59 | self.allow_classes[allow_class].append(i) | |
60 | else: | |
61 | self.allow_classes[allow_class] = ifacenames | |
62 | ||
63 | return 0 | |
64 | ||
65 | ||
66 | def process_source(self, lines, cur_idx, lineno): | |
67 | # Support regex | |
37c0543d | 68 | self.logger.debug('processing sourced line ..\'%s\'' %lines[cur_idx]) |
a6f80f0e | 69 | sourced_file = lines[cur_idx].split(' ', 2)[1] |
70 | if sourced_file is not None: | |
71 | for f in glob.glob(sourced_file): | |
eab25b7c | 72 | self.read_file(f) |
a6f80f0e | 73 | else: |
74 | self.logger.warn('unable to read source line at %d', lineno) | |
75 | ||
76 | return 0 | |
77 | ||
78 | def process_auto(self, lines, cur_idx, lineno): | |
79 | # XXX: Need to do more | |
80 | attrs = lines[cur_idx].split() | |
81 | if len(attrs) != 2: | |
82 | raise Exception('line%d: ' %lineno + 'incomplete \'auto\' line') | |
83 | ||
84 | self.auto_ifaces.append(lines[cur_idx].split()[1]) | |
85 | ||
86 | return 0 | |
87 | ||
88 | ||
89 | def process_iface(self, lines, cur_idx, lineno): | |
90 | lines_consumed = 0 | |
579b3f25 | 91 | line_idx = cur_idx |
a6f80f0e | 92 | |
93 | ifaceobj = iface() | |
94 | ||
95 | iface_line = lines[cur_idx].strip('\n ') | |
96 | iface_attrs = iface_line.split() | |
739f665b | 97 | ifacename = iface_attrs[1] |
a6f80f0e | 98 | |
99 | ifaceobj.raw_lines.append(iface_line) | |
100 | ||
101 | iface_config = collections.OrderedDict() | |
102 | for line_idx in range(cur_idx + 1, len(lines)): | |
103 | l = lines[line_idx].strip('\n\t ') | |
104 | ||
105 | if self.ignore_line(l) == 1: | |
106 | continue | |
107 | ||
a6f80f0e | 108 | if self.is_keyword(l.split()[0]) == True: |
109 | line_idx -= 1 | |
110 | break | |
111 | ||
eab25b7c | 112 | ifaceobj.raw_lines.append(l) |
113 | ||
739f665b | 114 | # preprocess vars (XXX: only preprocesses $IFACE for now) |
115 | l = re.sub(r'\$IFACE', ifacename, l) | |
116 | ||
eab25b7c | 117 | attrs = l.split(' ', 1) |
118 | if len(attrs) < 2: | |
119 | self.logger.warn('invalid syntax at line %d' %(line_idx + 1)) | |
120 | continue | |
a6f80f0e | 121 | |
eab25b7c | 122 | if iface_config.get(attrs[0]) == None: |
123 | iface_config[attrs[0]] = [attrs[1].strip(' ')] | |
a6f80f0e | 124 | else: |
eab25b7c | 125 | iface_config[attrs[0]].append(attrs[1].strip(' ')) |
a6f80f0e | 126 | |
127 | lines_consumed = line_idx - cur_idx | |
128 | ||
129 | # Create iface object | |
37c0543d | 130 | if ifacename.find(':') != -1: |
131 | ifaceobj.set_name(ifacename.split(':')[0]) | |
132 | else: | |
133 | ifaceobj.set_name(ifacename) | |
134 | ||
a6f80f0e | 135 | ifaceobj.set_config(iface_config) |
136 | ifaceobj.generate_env() | |
137 | if len(iface_attrs) > 2: | |
138 | ifaceobj.set_addr_family(iface_attrs[2]) | |
139 | ifaceobj.set_addr_method(iface_attrs[3]) | |
140 | ||
141 | if ifaceobj.get_name() in self.auto_ifaces: | |
142 | ifaceobj.set_auto() | |
143 | ||
144 | classes = ifaceobj.set_classes( | |
145 | self.get_allow_classes_for_iface(ifaceobj.get_name())) | |
146 | if classes is not None and len(classes) > 0: | |
147 | for c in classes: | |
148 | ifaceobj.set_class(c) | |
149 | ||
150 | # Call iface found callback | |
37c0543d | 151 | #self.logger.debug('saving interface %s' %ifaceobj.get_name()) |
a6f80f0e | 152 | self.callbacks.get('iface_found')(ifaceobj) |
153 | ||
154 | return lines_consumed # Return next index | |
155 | ||
156 | ||
157 | network_elems = { 'source' : process_source, | |
158 | 'allow' : process_allow, | |
159 | 'auto' : process_auto, | |
160 | 'iface' : process_iface} | |
161 | ||
162 | ||
163 | def is_keyword(self, str): | |
164 | ||
165 | # The additional split here is for allow- keyword | |
166 | tmp_str = str.split('-')[0] | |
167 | if tmp_str in self.network_elems.keys(): | |
168 | return 1 | |
169 | ||
170 | return 0 | |
171 | ||
172 | def get_keyword_func(self, str): | |
173 | tmp_str = str.split('-')[0] | |
174 | ||
175 | return self.network_elems.get(tmp_str) | |
176 | ||
177 | def get_allow_classes_for_iface(self, ifacename): | |
178 | classes = [] | |
179 | for class_name, ifacenames in self.allow_classes.items(): | |
180 | if ifacename in ifacenames: | |
181 | classes.append(class_name) | |
182 | ||
183 | return classes | |
184 | ||
579b3f25 | 185 | def process_filedata(self, filedata): |
a6f80f0e | 186 | lineno = 0 |
187 | line_idx = 0 | |
188 | lines_consumed = 0 | |
189 | ||
579b3f25 | 190 | raw_lines = filedata.split('\n') |
191 | lines = [l.strip(' \n') for l in raw_lines] | |
a6f80f0e | 192 | |
579b3f25 | 193 | while (line_idx < len(lines)): |
194 | lineno = lineno + 1 | |
a6f80f0e | 195 | |
579b3f25 | 196 | if self.ignore_line(lines[line_idx]): |
197 | line_idx += 1 | |
198 | continue | |
199 | ||
200 | words = lines[line_idx].split() | |
a6f80f0e | 201 | |
579b3f25 | 202 | # Check if first element is a supported keyword |
203 | if self.is_keyword(words[0]): | |
204 | keyword_func = self.get_keyword_func(words[0]) | |
205 | lines_consumed = keyword_func(self, lines, line_idx, lineno) | |
206 | line_idx += lines_consumed | |
207 | else: | |
208 | self.logger.warning('could not process line %s' %l + ' at' + | |
209 | ' lineno %d' %lineno) | |
a6f80f0e | 210 | |
579b3f25 | 211 | line_idx += 1 |
a6f80f0e | 212 | |
579b3f25 | 213 | return 0 |
a6f80f0e | 214 | |
579b3f25 | 215 | def run_template_engine(self, textdata): |
216 | try: | |
217 | from mako.template import Template | |
218 | except: | |
37c0543d | 219 | self.logger.warning('template engine mako not found. ' + |
220 | 'skip template parsing ..'); | |
579b3f25 | 221 | return textdata |
a6f80f0e | 222 | |
579b3f25 | 223 | t = Template(text=textdata, output_encoding='utf-8') |
224 | return t.render() | |
a6f80f0e | 225 | |
579b3f25 | 226 | def read_file(self, filename=None): |
227 | ifaces_file = filename | |
228 | if ifaces_file == None: | |
229 | ifaces_file=self.ifaces_file | |
230 | ||
37c0543d | 231 | self.logger.debug('reading interfaces file %s' %ifaces_file) |
579b3f25 | 232 | f = open(ifaces_file) |
233 | filedata = f.read() | |
234 | f.close() | |
235 | ||
236 | # process line continuations | |
237 | filedata = ' '.join(d.strip() for d in filedata.split('\\')) | |
238 | ||
239 | # run through template engine | |
240 | filedata = self.run_template_engine(filedata) | |
241 | ||
242 | self.process_filedata(filedata) | |
a6f80f0e | 243 | |
244 | ||
245 | def load(self, filename=None): | |
a6f80f0e | 246 | return self.read_file(filename) |