Revert "[kvm/dpdk] Switch vlan mode to vxlan"
[fuel.git] / deploy / templater.py
1 #!/usr/bin/env python
2 ###############################################################################
3 # Copyright (c) 2016 Ericsson AB and others.
4 # peter.barabas@ericsson.com
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ###############################################################################
10
11
12 import io
13 import re
14 import yaml
15 import urllib2
16 from common import(
17     err,
18     ArgParser,
19 )
20
21
22 TAG_START = '%{'
23 TAG_END = '}'
24 DELIMITER = '/'
25
26
27 class Templater(object):
28     def __init__(self, base_file, template_file, output_file):
29         self.template_file = template_file
30         self.output_file = output_file
31         self.base = self.load_yaml(base_file)
32
33     def is_url(self, filespec):
34         regex = re.compile('^([^/:]+)://')
35         return re.search(regex, filespec)
36
37     def load_template(self, filespec):
38         try:
39             if(self.is_url(filespec)):
40                 response = urllib2.urlopen(filespec)
41                 return response.read()
42             else:
43                 with io.open(filespec) as f:
44                     return f.readlines()
45         except Exception as error:
46             err('Error opening template file: %s' % error)
47
48     def load_yaml(self, filespec):
49         try:
50             if(self.is_url(filespec)):
51                 response = urllib2.urlopen(filespec)
52                 return yaml.load(response)
53             else:
54                 with io.open(filespec) as f:
55                     return yaml.load(f)
56         except Exception as error:
57             err('Error opening YAML file: %s' % error)
58
59     def save_yaml(self, filename, content):
60         try:
61             with io.open(filename, 'w') as yaml_file:
62                 yaml_file.write(content)
63         except Exception as error:
64             err('Error writing YAML file: %s' % error)
65
66     def get_indent(self, line):
67         return len(line) - len(line.lstrip(' '))
68
69     def format_fragment(self, fragment, indent):
70         result = ''
71         is_first_line = True
72
73         for line in fragment.splitlines():
74             # Skip indenting the first line as it is already indented
75             if is_first_line:
76                 line += '\n'
77                 is_first_line = False
78             else:
79                 line = ' ' * indent + line + '\n'
80
81             result += line
82
83         return result.rstrip('\n')
84
85     def format_substitution(self, string):
86         if isinstance(string, basestring):
87             return string
88         else:
89             return yaml.dump(string, default_flow_style=False)
90
91     def parse_interface_tag(self, tag):
92         # Remove 'interface(' prefix, trailing ')' and split arguments
93         args = tag[len('interface('):].rstrip(')').split(',')
94
95         if len(args) == 1 and not args[0]:
96             err('No arguments for interface().')
97         elif len(args) == 2 and (not args[0] or not args[1]):
98             err('Empty argument for interface().')
99         elif len(args) > 2:
100             err('Too many arguments for interface().')
101         else:
102             return args
103
104     def get_interface_from_network(self, interfaces, network):
105         nics = self.base[interfaces]
106         for nic in nics:
107             if network in nics[nic]:
108                 return nic
109
110         err('Network not found: %s' % network)
111
112     def get_role_interfaces(self, role):
113         nodes = self.base['nodes']
114         for node in nodes:
115             if role in node['role']:
116                 return node['interfaces']
117
118         err('Role not found: %s' % role)
119
120     def lookup_interface(self, args):
121         nodes = self.base['nodes']
122
123         if len(args) == 1:
124             interfaces = nodes[0]['interfaces']
125         if len(args) == 2:
126             interfaces = self.get_role_interfaces(args[1])
127
128         return self.get_interface_from_network(interfaces, args[0])
129
130     def parse_include_tag(self, tag):
131         # Remove 'include(' prefix and trailing ')'
132         filename = tag[len('include('):].rstrip(')')
133
134         if not filename:
135             err('No argument for include().')
136
137         return filename
138
139     def include_file(self, filename):
140         fragment = self.load_yaml(filename)
141         return yaml.dump(fragment, default_flow_style=False)
142
143     def parse_tag(self, tag, indent):
144         fragment = ''
145
146         if 'interface(' in tag:
147             args = self.parse_interface_tag(tag)
148             fragment = self.lookup_interface(args)
149         elif 'include(' in tag:
150             filename = self.parse_include_tag(tag)
151             fragment = self.include_file(filename)
152         else:
153             path = tag.split(DELIMITER)
154             fragment = self.base
155             for i in path:
156                 if i in fragment:
157                     fragment = fragment.get(i)
158                 else:
159                     err('Error: key "%s" does not exist in base YAML file' % i)
160
161             fragment = self.format_substitution(fragment)
162
163         return self.format_fragment(fragment, indent)
164
165     def run(self):
166         result = ''
167
168         regex = re.compile(re.escape(TAG_START) + r'([a-z].+)' + re.escape(TAG_END),
169                            flags=re.IGNORECASE)
170         for line in self.load_template(self.template_file):
171             indent = self.get_indent(line)
172             result += re.sub(regex,
173                              lambda match: self.parse_tag(match.group(1), indent),
174                              line)
175
176         self.save_yaml(self.output_file, result)
177
178
179 def parse_arguments():
180     description = '''Process 'template_file' using 'base_file' as source for
181 template variable substitution and write the results to 'output_file'.'''
182
183     parser = ArgParser(prog='python %s' % __file__,
184                        description=description)
185     parser.add_argument('base_file',
186                         help='Base YAML file or URL')
187     parser.add_argument('template_file',
188                         help='Template file or URL')
189     parser.add_argument('output_file',
190                         help='Output filename')
191
192     args = parser.parse_args()
193     return(args.base_file, args.template_file, args.output_file)
194
195
196 def main():
197     base_file, template_file, output_file = parse_arguments()
198
199     templater = Templater(base_file, template_file, output_file)
200     templater.run()
201
202
203 if __name__ == '__main__':
204     main()