Yardstick TC082: move sample test case perf.yaml
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / iniparser.py
1 # Copyright 2012 OpenStack Foundation
2 #
3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
4 #    not use this file except in compliance with the License. You may obtain
5 #    a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 #    Unless required by applicable law or agreed to in writing, software
10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 #    License for the specific language governing permissions and limitations
13 #    under the License.
14
15
16 class ParseError(Exception):
17     def __init__(self, message, line_no, line):
18         self.msg = message
19         self.line = line
20         self.line_no = line_no
21
22     def __str__(self):
23         return 'at line %d, %s: %r' % (self.line_no, self.msg, self.line)
24
25
26 class BaseParser(object):
27
28     PARSE_EXC = ParseError
29
30     def __init__(self):
31         super(BaseParser, self).__init__()
32         self.line_no = 0
33
34     def _assignment(self, key, value):
35         self.assignment(key, value)
36         return None, []
37
38     def _get_section(self, line):
39         if not line.endswith(']'):
40             return self.error_no_section_end_bracket(line)
41         if len(line) <= 2:
42             return self.error_no_section_name(line)
43
44         return line[1:-1]
45
46     def _split_key_value(self, line):
47         colon = line.find(':')
48         equal = line.find('=')
49         if colon < 0 and equal < 0:
50             return self.error_invalid_assignment(line)
51
52         if colon < 0 or (0 <= equal < colon):
53             key, value = line[:equal], line[equal + 1:]
54         else:
55             key, value = line[:colon], line[colon + 1:]
56
57         value = value.strip()
58         if value and value[0] == value[-1] and value.startswith(("\"", "'")):
59             value = value[1:-1]
60         return key.strip(), [value]
61
62     def _single_line_parse(self, line, key, value):
63         self.line_no += 1
64
65         if line.startswith(('#', ';')):
66             self.comment(line[1:].strip())
67             return key, value
68
69         active, _, comment = line.partition(';')
70         self.comment(comment.strip())
71
72         if not active:
73             # Blank line, ends multi-line values
74             if key:
75                 key, value = self._assignment(key, value)
76             return key, value
77
78         if active.startswith((' ', '\t')):
79             # Continuation of previous assignment
80             if key is None:
81                 return self.error_unexpected_continuation(line)
82
83             value.append(active.lstrip())
84             return key, value
85
86         if key:
87             # Flush previous assignment, if any
88             key, value = self._assignment(key, value)
89
90         if active.startswith('['):
91             # Section start
92             section = self._get_section(active)
93             if section:
94                 self.new_section(section)
95
96         else:
97             key, value = self._split_key_value(active)
98             if not key:
99                 return self.error_empty_key(line)
100
101         return key, value
102
103     def parse(self, line_iter=None):
104         if line_iter is None:
105             return
106
107         key = None
108         value = []
109
110         for line in line_iter:
111             key, value = self._single_line_parse(line, key, value)
112
113         if key:
114             # Flush previous assignment, if any
115             self._assignment(key, value)
116
117     def assignment(self, key, value):
118         """Called when a full assignment is parsed."""
119         raise NotImplementedError()
120
121     def new_section(self, section):
122         """Called when a new section is started."""
123         raise NotImplementedError()
124
125     def comment(self, comment):
126         """Called when a comment is parsed."""
127         pass
128
129     def make_parser_error(self, template, line):
130         raise self.PARSE_EXC(template, self.line_no, line)
131
132     def error_invalid_assignment(self, line):
133         self.make_parser_error("No ':' or '=' found in assignment", line)
134
135     def error_empty_key(self, line):
136         self.make_parser_error('Key cannot be empty', line)
137
138     def error_unexpected_continuation(self, line):
139         self.make_parser_error('Unexpected continuation line', line)
140
141     def error_no_section_end_bracket(self, line):
142         self.make_parser_error('Invalid section (must end with ])', line)
143
144     def error_no_section_name(self, line):
145         self.make_parser_error('Empty section name', line)
146
147
148 class ConfigParser(BaseParser):
149     """Parses a single config file, populating 'sections' to look like:
150
151         {'DEFAULT': {'key': [value, ...], ...},
152          ...}
153     """
154
155     def __init__(self, filename, sections):
156         super(ConfigParser, self).__init__()
157         self.filename = filename
158         self.sections = sections
159         self.section = None
160
161     def parse(self, line_iter=None):
162         with open(self.filename) as f:
163             return super(ConfigParser, self).parse(f)
164
165     def new_section(self, section):
166         self.section = section
167         self.sections.setdefault(self.section, [])
168
169     def assignment(self, key, value):
170         if not self.section:
171             raise self.error_no_section()
172
173         value = '\n'.join(value)
174         self.sections[self.section].append([key, value])
175
176     def error_no_section(self):
177         self.make_parser_error('Section must be started before assignment', '')