two more changes for python3 in the bytes vs str area
[nodemanager.git] / conf_files.py
1 #!/usr/bin/env python3
2
3 # pylint: disable=c0111
4
5 """
6 update local configuration files from PLC
7 """
8
9 import grp
10 import os
11 import pwd
12 from hashlib import sha1 as sha
13 import xmlrpc.client
14
15 import curlwrapper
16 import logger
17 import tools
18 from config import Config
19
20 # right after net
21 priority = 2
22
23 class conf_files:
24     def __init__(self, noscripts=False):
25         self.config = Config()
26         self.noscripts = noscripts
27         self.data = None
28
29     def checksum(self, path):
30         try:
31             with open(path) as feed:
32                 return sha(feed.read().encode()).digest()
33         except IOError:
34             return None
35
36     def system(self, cmd):
37         if not self.noscripts and cmd:
38             logger.verbose('conf_files: running command %s' % cmd)
39             return tools.fork_as(None, os.system, cmd)
40         else: return 0
41
42     def update_conf_file(self, cf_rec):
43         if not cf_rec['enabled']:
44             return
45         dest = cf_rec['dest']
46         err_cmd = cf_rec['error_cmd']
47         mode = int(cf_rec['file_permissions'], base=8)
48         try:
49             uid = pwd.getpwnam(cf_rec['file_owner'])[2]
50         except:
51             logger.log('conf_files: cannot find user %s -- %s not updated'
52                        %(cf_rec['file_owner'], dest))
53             return
54         try:
55             gid = grp.getgrnam(cf_rec['file_group'])[2]
56         except:
57             logger.log('conf_files: cannot find group %s -- %s not updated'
58                        %(cf_rec['file_group'], dest))
59             return
60         url = 'https://%s/%s' % (self.config.PLC_BOOT_HOST, cf_rec['source'])
61         # set node_id at the end of the request - hacky
62         if tools.node_id():
63             if url.find('?') > 0:
64                 url += '&'
65             else:
66                 url += '?'
67             url += "node_id=%d"%tools.node_id()
68         else:
69             logger.log('conf_files: %s -- WARNING, cannot add node_id to request'
70                        % dest)
71         try:
72             logger.verbose("conf_files: retrieving URL=%s"%url)
73             contents = curlwrapper.retrieve(url, self.config.cacert)
74         except xmlrpc.client.ProtocolError as e:
75             logger.log('conf_files: failed to retrieve %s from %s, skipping' % (dest, url))
76             return
77         if not cf_rec['always_update'] and sha(contents).digest() == self.checksum(dest):
78             return
79         if self.system(cf_rec['preinstall_cmd']):
80             self.system(err_cmd)
81             if not cf_rec['ignore_cmd_errors']:
82                 return
83         logger.log('conf_files: installing file %s from %s' % (dest, url))
84         try:
85             os.makedirs(os.path.dirname(dest))
86         except OSError:
87             pass
88         tools.write_file(dest, lambda f: f.write(contents.decode()),
89                          mode=mode, uidgid=(uid, gid))
90         if self.system(cf_rec['postinstall_cmd']):
91             self.system(err_cmd)
92
93     def run_once(self, data):
94         if "conf_files" in data:
95             for file in data['conf_files']:
96                 try:
97                     self.update_conf_file(file)
98                 except:
99                     logger.log_exc("conf_files: failed to update conf_file")
100         else:
101             logger.log_missing_data("conf_files.run_once", 'conf_files')
102
103
104 def start():
105     pass
106
107
108 def GetSlivers(data, config=None, plc=None):
109     logger.log("conf_files: Running.")
110     instance = conf_files()
111     instance.run_once(data)
112     logger.log("conf_files: Done.")
113
114
115 def main():
116     from argparse import ArgumentParser
117     parser = ArgumentParser()
118     parser.add_argument('-f', '--config', action='store', dest='config',
119                         default='/etc/planetlab/plc_config',
120                         help='PLC configuration file')
121     parser.add_argument('-k', '--session', action='store', dest='session',
122                         default='/etc/planetlab/session',
123                         help='API session key (or file)')
124     parser.add_argument('--noscripts', action='store_true', dest='noscripts',
125                         default=False,
126                         help='Do not run pre- or post-install scripts')
127     args = parser.parse_args()
128
129     # Load /etc/planetlab/plc_config
130     config = Config(args.config)
131
132     # Load /etc/planetlab/session
133     if os.path.exists(args.session):
134         with open(args.session) as feed:
135             session = feed.read().strip()
136     else:
137         session = args.session
138
139     # Initialize XML-RPC client
140     from plcapi import PLCAPI
141     plc = PLCAPI(config.plc_api_uri, config.cacert, auth=session)
142     data = plc.GetSlivers()
143
144     instance = conf_files(args.noscripts)
145     instance.run_once(data)
146
147
148 if __name__ == '__main__':
149     main()