add some basic logging output to certmaster
[certmaster.git] / certmaster / overlord / inventory.py
1 ##
2 ## func inventory app.
3 ## use func to collect inventory data on anything, yes, anything
4 ##
5 ## Copyright 2007, Red Hat, Inc
6 ## Michael DeHaan <mdehaan@redhat.com>
7 ## +AUTHORS
8 ##
9 ## This software may be freely redistributed under the terms of the GNU
10 ## general public license.
11 ##
12 ## You should have received a copy of the GNU General Public License
13 ## along with this program; if not, write to the Free Software
14 ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 ##
16
17 import os.path
18 import time
19 import optparse
20 import sys
21 import pprint
22 import xmlrpclib
23 from func.minion import sub_process
24 import func.overlord.client as func_client
25 import func.utils as utils
26
27 DEFAULT_TREE = "/var/lib/func/inventory/"
28
29
30 class FuncInventory(object):
31
32 def __init__(self):
33 pass
34
35 def run(self,args):
36
37 p = optparse.OptionParser()
38 p.add_option("-v", "--verbose",
39 dest="verbose",
40 action="store_true",
41 help="provide extra output")
42 p.add_option("-s", "--server-spec",
43 dest="server_spec",
44 default="*",
45 help="run against specific servers, default: '*'")
46 p.add_option("-m", "--methods",
47 dest="methods",
48 default="inventory",
49 help="run inventory only on certain function names, default: 'inventory'")
50 p.add_option("-M", "--modules",
51 dest="modules",
52 default="all",
53 help="run inventory only on certain module names, default: 'all'")
54 p.add_option("-t", "--tree",
55 dest="tree",
56 default=DEFAULT_TREE,
57 help="output results tree here, default: %s" % DEFAULT_TREE)
58 p.add_option("-n", "--no-git",
59 dest="nogit",
60 action="store_true",
61 help="disable useful change tracking features")
62 p.add_option("-x", "--xmlrpc", dest="xmlrpc",
63 help="output data using XMLRPC format",
64 action="store_true")
65 p.add_option("-j", "--json", dest="json",
66 help="output data using JSON",
67 action="store_true")
68
69
70 (options, args) = p.parse_args(args)
71 self.options = options
72
73 filtered_module_list = options.modules.split(",")
74 filtered_function_list = options.methods.split(",")
75
76 self.git_setup(options)
77
78 # see what modules each host provides (as well as what hosts we have)
79 host_methods = func_client.Client(options.server_spec).system.list_methods()
80
81 # call all remote info methods and handle them
82 if options.verbose:
83 print "- scanning ..."
84 # for (host, modules) in host_modules.iteritems():
85
86 for (host, methods) in host_methods.iteritems():
87
88 if utils.is_error(methods):
89 print "-- connection refused: %s" % host
90 break
91
92 for each_method in methods:
93
94 #if type(each_method) == int:
95 # if self.options.verbose:
96 # print "-- connection refused: %s" % host
97 # break
98
99 tokens = each_method.split(".")
100 module_name = ".".join(tokens[:-1])
101 method_name = tokens[-1]
102
103 if not "all" in filtered_module_list and not module_name in filtered_module_list:
104 continue
105
106 if not "all" in filtered_function_list and not method_name in filtered_function_list:
107 continue
108
109 client = func_client.Client(host,noglobs=True) # ,noglobs=True)
110 results = getattr(getattr(client,module_name),method_name)()
111 if self.options.verbose:
112 print "-- %s: running: %s %s" % (host, module_name, method_name)
113 self.save_results(options, host, module_name, method_name, results)
114 self.git_update(options)
115 return 1
116
117 def format_return(self, data):
118 """
119 The call module supports multiple output return types, the default is pprint.
120 """
121
122 # special case... if the return is a string, just print it straight
123 if type(data) == str:
124 return data
125
126 if self.options.xmlrpc:
127 return xmlrpclib.dumps((data,""))
128
129 if self.options.json:
130 try:
131 import simplejson
132 return simplejson.dumps(data)
133 except ImportError:
134 print "ERROR: json support not found, install python-simplejson"
135 sys.exit(1)
136
137 return pprint.pformat(data)
138
139 # FUTURE: skvidal points out that guest symlinking would be an interesting feature
140
141 def save_results(self, options, host_name, module_name, method_name, results):
142 dirname = os.path.join(options.tree, host_name, module_name)
143 if not os.path.exists(dirname):
144 os.makedirs(dirname)
145 filename = os.path.join(dirname, method_name)
146 results_file = open(filename,"w+")
147 data = self.format_return(results)
148 results_file.write(data)
149 results_file.close()
150
151 def git_setup(self,options):
152 if options.nogit:
153 return
154 if not os.path.exists("/usr/bin/git"):
155 print "git-core is not installed, so no change tracking is available."
156 print "use --no-git or, better, just install it."
157 sys.exit(411)
158
159 if not os.path.exists(options.tree):
160 os.makedirs(options.tree)
161 dirname = os.path.join(options.tree, ".git")
162 if not os.path.exists(dirname):
163 if options.verbose:
164 print "- initializing git repo: %s" % options.tree
165 cwd = os.getcwd()
166 os.chdir(options.tree)
167 rc1 = sub_process.call(["/usr/bin/git", "init"], shell=False)
168 # FIXME: check rc's
169 os.chdir(cwd)
170 else:
171 if options.verbose:
172 print "- git already initialized: %s" % options.tree
173
174 def git_update(self,options):
175 if options.nogit:
176 return
177 else:
178 if options.verbose:
179 print "- updating git"
180 mytime = time.asctime()
181 cwd = os.getcwd()
182 os.chdir(options.tree)
183 rc1 = sub_process.call(["/usr/bin/git", "add", "*" ], shell=False)
184 rc2 = sub_process.call(["/usr/bin/git", "commit", "-a", "-m", "Func-inventory update: %s" % mytime], shell=False)
185 # FIXME: check rc's
186 os.chdir(cwd)
187
188
189 if __name__ == "__main__":
190 inv = FuncInventory()
191 inv.run(sys.argv)