4 # Copyright (c) 2018 Orange
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
20 Provides utilities to display oom (sub)modules resources stats
26 from fnmatch import fnmatch as match
30 from tabulate import tabulate
31 except ImportError as e:
32 message = "Warning: cannot import tabulate module (): {}\n".format(str(e))
33 sys.stderr.write(message)
34 def tabulate(lines, headers, tablefmt=None):
37 lenco = map(len, headers)
40 lenco[i] = max(lenco[i], len(str(line[i])))
42 fmt = map(lambda n: "{{:<{}}}".format(n), map(lambda i: i+2, lenco))
44 sep = map(lambda x: '-'*(x+2), lenco)
46 output = [fmt.format(*headers), fmt.format(*sep)]
48 output.append(fmt.format(*line))
49 return "\n".join(output)
53 ''' Get the list of values.yaml files '''
55 for dirname, dirnames, filenames in os.walk(root):
56 for filename in filenames:
57 if filename == 'values.yaml':
58 a.append((dirname, filename))
60 if '.git' in dirnames:
61 # don't go into any .git directories.
62 dirnames.remove('.git')
66 def keys(dic, prefix=None):
67 ''' recursively traverse the specified dict to collect existing keys '''
70 for k, v in dic.items():
72 k = '.'.join((prefix, k))
73 if isinstance(v, dict):
82 class to access to oom (sub)module (aka project) resources
85 def __init__(self, dirname, filename):
86 self.dirname = os.path.normpath(dirname)
87 self.name = self.explicit()
88 self.filename = os.path.join(dirname, filename)
93 ''' load resources from yaml description '''
94 with open(self.filename, 'r') as istream:
96 v = yaml.load(istream)
98 self.resources = v.get('resources', None)
99 except Exception as e:
104 ''' return an explicit name for the project '''
106 head, name = os.path.split(self.dirname)
110 head, tail = os.path.split(head)
117 index = path.index('charts') if 'charts' in path else None
119 name = os.path.join(path[index-1], name)
122 def __contains__(self, key):
123 params = self.resources
125 for k in key.split('.'):
126 if params and k in params:
132 def __getitem__(self, key):
133 params = self.resources
134 for k in key.split('.'):
137 if params != self.resources:
140 def get(self, key, default="-"):
141 """ mimic dict method """
147 """ mimic dict method """
148 return keys(self.resources)
155 def usage(status=None):
157 arg0 = os.path.basename(os.path.abspath(sys.argv[0]))
158 print("""Usage: {} [options] <root-directory>""".format(arg0))
162 "-h, --help Show this help message and exit\n"
163 "-t, --table <format> Use the specified format to display the result table.\n"
164 " Valid formats are those from the python `tabulate'\n"
165 " module. When not available, a basic builtin tabular\n"
166 " function is used and this field has no effect\n"
167 "-f, --fields Comma separated list of resources fields to display.\n"
168 " You may use wildcard patterns, eg small.*. Implicit\n"
169 " value is *, ie all available fields will be used\n"
171 " # oomstat /opt/oom/kubernetes\n"
172 " # oomstat -f small.\\* /opt/oom/kubernetes\n"
173 " # oomstat -f '*requests.*' -t fancy_grid /opt/oom/kubernetes\n"
174 " # oomstat -f small.requests.cpu,small.requests.memory /opt/oom/kubernetes\n"
176 if status is not None:
181 """ read options from cmdline """
182 opts, args = getopt.getopt(sys.argv[1:],
184 ["help", "fields=", "table="])
193 for opt, arg in opts:
194 if opt in ("-h", '--help'):
196 elif opt in ("-f", "--fields"):
197 fields = arg.split(',')
198 elif opt in ("-t", "--table"):
201 return root, table, fields, patterns
207 root, table, fields, patterns = getopts()
208 except getopt.GetoptError as e:
209 print("Error: {}".format(e))
214 for dirname, filename in values(root):
215 projects.append(Project(dirname, filename))
217 # check if we want to use pattern matching (wildcard only)
218 if fields and reduce(lambda x, y: x or y,
219 map(lambda string: '*' in string, fields)):
223 # if fields are not specified or patterns are used, discover available fields
224 # and use them (sort for readability)
225 if patterns or not fields:
226 avail = sorted(set(reduce(lambda x, y: x+y,
227 map(lambda p: p.keys(), projects))))
229 for pattern in patterns:
230 fields += filter(lambda string: match(string, pattern), avail)
234 # collect values for each project
235 results = map(lambda project: [project.name] + map(project.get,
241 headers = ['project'] + fields
242 print(tabulate(sorted(results), headers, tablefmt=table))