From a7b2ee30e615a8567d953b644bc37ec07759fcdf Mon Sep 17 00:00:00 2001 From: Nicolas Edel Date: Fri, 23 Nov 2018 08:42:21 +0100 Subject: Better handling of wrong root directory spec Also rename and use a .py extension Change-Id: I98d6820dc21faa27ae3dd88cfbe82a65d62d9ba4 Issue-ID: OOM-1527 Signed-off-by: Nicolas Edel --- kubernetes/contrib/tools/oomstat | 245 ---------------------------------- kubernetes/contrib/tools/oomstat.py | 256 ++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 245 deletions(-) delete mode 100755 kubernetes/contrib/tools/oomstat create mode 100755 kubernetes/contrib/tools/oomstat.py (limited to 'kubernetes') diff --git a/kubernetes/contrib/tools/oomstat b/kubernetes/contrib/tools/oomstat deleted file mode 100755 index 82453b0a7c..0000000000 --- a/kubernetes/contrib/tools/oomstat +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env python - -# -# Copyright (c) 2018 Orange -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -""" -Provides utilities to display oom (sub)modules resources stats -""" - -import os -import sys -import getopt -from fnmatch import fnmatch as match -import yaml - -try: - from tabulate import tabulate -except ImportError as e: - message = "Warning: cannot import tabulate module (): {}\n".format(str(e)) - sys.stderr.write(message) - def tabulate(lines, headers, tablefmt=None): - fmt = "" - nbco = len(headers) - lenco = map(len, headers) - for line in lines: - for i in range(nbco): - lenco[i] = max(lenco[i], len(str(line[i]))) - - fmt = map(lambda n: "{{:<{}}}".format(n), map(lambda i: i+2, lenco)) - fmt = " ".join(fmt) - sep = map(lambda x: '-'*(x+2), lenco) - - output = [fmt.format(*headers), fmt.format(*sep)] - for line in lines: - output.append(fmt.format(*line)) - return "\n".join(output) - - -def values(root='.'): - ''' Get the list of values.yaml files ''' - a = [] - for dirname, dirnames, filenames in os.walk(root): - for filename in filenames: - if filename == 'values.yaml': - a.append((dirname, filename)) - - if '.git' in dirnames: - # don't go into any .git directories. - dirnames.remove('.git') - return a - - -def keys(dic, prefix=None): - ''' recursively traverse the specified dict to collect existing keys ''' - result = [] - if dic: - for k, v in dic.items(): - if prefix: - k = '.'.join((prefix, k)) - if isinstance(v, dict): - result += keys(v, k) - else: - result.append(k) - return result - - -class Project: - ''' - class to access to oom (sub)module (aka project) resources - ''' - - def __init__(self, dirname, filename): - self.dirname = os.path.normpath(dirname) - self.name = self.explicit() - self.filename = os.path.join(dirname, filename) - self.resources = None - self.load() - - def load(self): - ''' load resources from yaml description ''' - with open(self.filename, 'r') as istream: - try: - v = yaml.load(istream) - if v: - self.resources = v.get('resources', None) - except Exception as e: - print(e) - raise - - def explicit(self): - ''' return an explicit name for the project ''' - path = [] - head, name = os.path.split(self.dirname) - if not name: - return head - while head: - head, tail = os.path.split(head) - if tail: - path.append(tail) - else: - path.append(head) - head = None - path.reverse() - index = path.index('charts') if 'charts' in path else None - if index: - name = os.path.join(path[index-1], name) - return name - - def __contains__(self, key): - params = self.resources - if key: - for k in key.split('.'): - if params and k in params: - params = params[k] - else: - return False - return True - - def __getitem__(self, key): - params = self.resources - for k in key.split('.'): - if k in params: - params = params[k] - if params != self.resources: - return params - - def get(self, key, default="-"): - """ mimic dict method """ - if key in self: - return self[key] - return default - - def keys(self): - """ mimic dict method """ - return keys(self.resources) - - -# -# -# - -def usage(status=None): - """ usage doc """ - arg0 = os.path.basename(os.path.abspath(sys.argv[0])) - print("""Usage: {} [options] """.format(arg0)) - print(( - "\n" - "Options:\n" - "-h, --help Show this help message and exit\n" - "-t, --table Use the specified format to display the result table.\n" - " Valid formats are those from the python `tabulate'\n" - " module. When not available, a basic builtin tabular\n" - " function is used and this field has no effect\n" - "-f, --fields Comma separated list of resources fields to display.\n" - " You may use wildcard patterns, eg small.*. Implicit\n" - " value is *, ie all available fields will be used\n" - "Examples:\n" - " # oomstat /opt/oom/kubernetes\n" - " # oomstat -f small.\\* /opt/oom/kubernetes\n" - " # oomstat -f '*requests.*' -t fancy_grid /opt/oom/kubernetes\n" - " # oomstat -f small.requests.cpu,small.requests.memory /opt/oom/kubernetes\n" - )) - if status is not None: - sys.exit(status) - - -def getopts(): - """ read options from cmdline """ - opts, args = getopt.getopt(sys.argv[1:], - "hf:t:", - ["help", "fields=", "table="]) - if len(args) != 1: - usage(1) - - root = args[0] - table = None - fields = ['*'] - patterns = [] - - for opt, arg in opts: - if opt in ("-h", '--help'): - usage(0) - elif opt in ("-f", "--fields"): - fields = arg.split(',') - elif opt in ("-t", "--table"): - table = arg - - return root, table, fields, patterns - - -def main(): - """ main """ - try: - root, table, fields, patterns = getopts() - except getopt.GetoptError as e: - print("Error: {}".format(e)) - usage(1) - - # find projects - projects = [] - for dirname, filename in values(root): - projects.append(Project(dirname, filename)) - - # check if we want to use pattern matching (wildcard only) - if fields and reduce(lambda x, y: x or y, - map(lambda string: '*' in string, fields)): - patterns = fields - fields = [] - - # if fields are not specified or patterns are used, discover available fields - # and use them (sort for readability) - if patterns or not fields: - avail = sorted(set(reduce(lambda x, y: x+y, - map(lambda p: p.keys(), projects)))) - if patterns: - for pattern in patterns: - fields += filter(lambda string: match(string, pattern), avail) - else: - fields = avail - - # collect values for each project - results = map(lambda project: [project.name] + map(project.get, - fields), - projects) - - # and then print - if results: - headers = ['project'] + fields - print(tabulate(sorted(results), headers, tablefmt=table)) - - -main() diff --git a/kubernetes/contrib/tools/oomstat.py b/kubernetes/contrib/tools/oomstat.py new file mode 100755 index 0000000000..464290d3f6 --- /dev/null +++ b/kubernetes/contrib/tools/oomstat.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2018 Orange +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" +Provides utilities to display oom (sub)modules resources stats +""" + +import os +import sys +import getopt +from fnmatch import fnmatch as match +import yaml + +def info(thing): + if thing: + sys.stderr.write("{}\n".format(thing)) + +try: + from tabulate import tabulate +except ImportError as e: + info("Warning: cannot import tabulate module (): {}".format(str(e))) + def tabulate(lines, headers, tablefmt=None): + ''' basic tabulate function ''' + fmt = "" + nbco = len(headers) + lenco = map(len, headers) + for line in lines: + for i in range(nbco): + lenco[i] = max(lenco[i], len(str(line[i]))) + + fmt = map(lambda n: "{{:<{}}}".format(n), map(lambda i: i+2, lenco)) + fmt = " ".join(fmt) + sep = map(lambda x: '-'*(x+2), lenco) + + output = [fmt.format(*headers), fmt.format(*sep)] + for line in lines: + output.append(fmt.format(*line)) + return "\n".join(output) + + +def values(root='.'): + ''' Get the list of values.yaml files ''' + a = [] + for dirname, dirnames, filenames in os.walk(root): + for filename in filenames: + if filename == 'values.yaml': + a.append((dirname, filename)) + + if '.git' in dirnames: + # don't go into any .git directories. + dirnames.remove('.git') + return a + + +def keys(dic, prefix=None): + ''' recursively traverse the specified dict to collect existing keys ''' + result = [] + if dic: + for k, v in dic.items(): + if prefix: + k = '.'.join((prefix, k)) + if isinstance(v, dict): + result += keys(v, k) + else: + result.append(k) + return result + + +class Project: + ''' + class to access to oom (sub)module (aka project) resources + ''' + + def __init__(self, dirname, filename): + self.dirname = os.path.normpath(dirname) + self.name = self.explicit() + self.filename = os.path.join(dirname, filename) + self.resources = None + self.load() + + def load(self): + ''' load resources from yaml description ''' + with open(self.filename, 'r') as istream: + try: + v = yaml.load(istream) + if v: + self.resources = v.get('resources', None) + except Exception as e: + print(e) + raise + + def explicit(self): + ''' return an explicit name for the project ''' + path = [] + head, name = os.path.split(self.dirname) + if not name: + return head + while head: + head, tail = os.path.split(head) + if tail: + path.append(tail) + else: + path.append(head) + head = None + path.reverse() + index = path.index('charts') if 'charts' in path else None + if index: + name = os.path.join(path[index-1], name) + return name + + def __contains__(self, key): + params = self.resources + if key: + for k in key.split('.'): + if params and k in params: + params = params[k] + else: + return False + return True + + def __getitem__(self, key): + params = self.resources + for k in key.split('.'): + if k in params: + params = params[k] + if params != self.resources: + return params + + def get(self, key, default="-"): + """ mimic dict method """ + if key in self: + return self[key] + return default + + def keys(self): + """ mimic dict method """ + return keys(self.resources) + + +# +# +# + +def usage(status=None): + """ usage doc """ + arg0 = os.path.basename(os.path.abspath(sys.argv[0])) + print("""Usage: {} [options] """.format(arg0)) + print(( + "\n" + "Options:\n" + "-h, --help Show this help message and exit\n" + "-t, --table Use the specified format to display the result table.\n" + " Valid formats are those from the python `tabulate'\n" + " module. When not available, a basic builtin tabular\n" + " function is used and this field has no effect\n" + "-f, --fields Comma separated list of resources fields to display.\n" + " You may use wildcard patterns, eg small.*. Implicit\n" + " value is *, ie all available fields will be used\n" + "Examples:\n" + " # {0} /opt/oom/kubernetes\n" + " # {0} -f small.\\* /opt/oom/kubernetes\n" + " # {0} -f '*requests.*' -t fancy_grid /opt/oom/kubernetes\n" + " # {0} -f small.requests.cpu,small.requests.memory /opt/oom/kubernetes\n" + ).format(arg0)) + if status is not None: + sys.exit(status) + + +def getopts(): + """ read options from cmdline """ + opts, args = getopt.getopt(sys.argv[1:], + "hf:t:", + ["help", "fields=", "table="]) + if len(args) != 1: + usage(1) + + root = args[0] + table = None + fields = ['*'] + patterns = [] + + for opt, arg in opts: + if opt in ("-h", '--help'): + usage(0) + elif opt in ("-f", "--fields"): + fields = arg.split(',') + elif opt in ("-t", "--table"): + table = arg + + return root, table, fields, patterns + + +def main(): + """ main """ + try: + root, table, fields, patterns = getopts() + except getopt.GetoptError as e: + print("Error: {}".format(e)) + usage(1) + + if not os.path.isdir(root): + info("Cannot open {}: Not a directory".format(root)) + return + + # find projects + projects = [] + for dirname, filename in values(root): + projects.append(Project(dirname, filename)) + if not projects: + info("No projects found in {} directory".format(root)) + return + + # check if we want to use pattern matching (wildcard only) + if fields and reduce(lambda x, y: x or y, + map(lambda string: '*' in string, fields)): + patterns = fields + fields = [] + + # if fields are not specified or patterns are used, discover available fields + # and use them (sort for readability) + if patterns or not fields: + avail = sorted(set(reduce(lambda x, y: x+y, + map(lambda p: p.keys(), projects)))) + if patterns: + for pattern in patterns: + fields += filter(lambda string: match(string, pattern), avail) + else: + fields = avail + + # collect values for each project + results = map(lambda project: [project.name] + map(project.get, + fields), + projects) + + # and then print + if results: + headers = ['project'] + fields + print(tabulate(sorted(results), headers, tablefmt=table)) + + +main() -- cgit 1.2.3-korg