spaces after comma
[nodemanager.git] / plugins / vsys.py
1 """vsys configurator.  Maintains ACLs and script pipes inside vservers based on slice attributes."""
2
3 import os
4 import subprocess
5
6 import logger
7 import tools
8
9 VSYSCONF="/etc/vsys.conf"
10 VSYSBKEND="/vsys"
11
12 def start():
13     logger.log("vsys: plugin starting up...")
14
15 def GetSlivers(data, config=None, plc=None):
16     """
17     For each sliver with the vsys attribute:
18     set the script ACL, create the vsys directory in the slice, and restart vsys
19     """
20
21     if 'slivers' not in data:
22         logger.log_missing_data("vsys.GetSlivers", 'slivers')
23         return
24
25     # Touch ACLs and create dict of available
26     scripts = {}
27     for script in touchAcls(): scripts[script] = []
28     # slices that need to be written to the conf
29     slices = []
30     _restart = False
31     # Parse attributes and update dict of scripts
32     if 'slivers' not in data:
33         logger.log_missing_data("vsys.GetSlivers", 'slivers')
34         return
35     for sliver in data['slivers']:
36         for attribute in sliver['attributes']:
37             if attribute['tagname'] == 'vsys':
38                 if sliver['name'] not in slices:
39                     # add to conf
40                     slices.append(sliver['name'])
41                     _restart = createVsysDir(sliver['name']) or _restart
42                 if attribute['value'] in scripts.keys():
43                     scripts[attribute['value']].append(sliver['name'])
44
45     # Write the conf
46     _restart = writeConf(slices, parseConf()) or _restart
47     # Write out the ACLs
48     if writeAcls(scripts, parseAcls()) or _restart:
49         restartService()
50
51 # check for systemctl, use it if present
52 # keyword being 'start', 'stop' or 'restart'
53 def handleService(keyword):
54     if tools.has_systemctl():
55         logger.log("vsys: %s'ing vsys service through systemctl"%keyword)
56         return logger.log_call(["systemctl", keyword, "vsys"], timeout=5)
57     else:
58         logger.log("vsys: %s'ing vsys service through /etc/init.d/vsys"%keyword)
59         return logger.log_call(["/etc/init.d/vsys", keyword], timeout=5)
60
61 def startService():
62     return handleService ('start')
63 def stopService():
64     return handleService ('stop')
65 def restartService():
66     return handleService ('restart')
67
68 def createVsysDir(sliver):
69     """Create /vsys directory in slice.  Update vsys conf file."""
70     try:
71         os.mkdir("/vservers/%s/vsys" % sliver)
72         return True
73     except OSError:
74         return False
75
76
77 def touchAcls():
78     '''Creates empty acl files for scripts.
79     To be ran in case of new scripts that appear in the backend.
80     Returns list of available scripts.'''
81     acls = []
82     scripts = []
83     for (root, dirs, files) in os.walk(VSYSBKEND):
84         for file in files:
85             # ingore scripts that start with local_
86             if file.startswith("local_"): continue
87             if file.endswith(".acl"):
88                 acls.append(file.replace(".acl", ""))
89             else:
90                 scripts.append(file)
91     for new in (set(scripts) - set(acls)):
92         logger.log("vsys: Found new script %s.  Writing empty acl." % new)
93         f = open("%s/%s.acl" %(VSYSBKEND, new), "w")
94         f.write("\n")
95         f.close()
96
97     return scripts
98
99
100 def writeAcls(currentscripts, oldscripts):
101     '''Creates .acl files for script in the script repo.'''
102     # Check each oldscript entry to see if we need to modify
103     _restartvsys = False
104     # for iteritems along dict(oldscripts), if length of values
105     # not the same as length of values of new scripts,
106     # and length of non intersection along new scripts is not 0,
107     # then dicts are different.
108     for (acl, oldslivers) in oldscripts.iteritems():
109         try:
110             if (len(oldslivers) != len(currentscripts[acl])) or \
111             (len(set(oldslivers) - set(currentscripts[acl])) != 0):
112                 _restartvsys = True
113                 logger.log("vsys: Updating %s.acl w/ slices %s" % (acl, currentscripts[acl]))
114                 f = open("%s/%s.acl" % (VSYSBKEND, acl), "w")
115                 for slice in currentscripts[acl]: f.write("%s\n" % slice)
116                 f.close()
117         except KeyError:
118             logger.log("vsys: #:)# Warning,Not a valid Vsys script,%s"%acl)
119     # Trigger a restart
120     return _restartvsys
121
122
123 def parseAcls():
124     '''Parse the frontend script acls.  Return {script: [slices]} in conf.'''
125     # make a dict of what slices are in what acls.
126     scriptacls = {}
127     for (root, dirs, files) in os.walk(VSYSBKEND):
128         for file in files:
129             if file.endswith(".acl") and not file.startswith("local_"):
130                 with open(root+"/"+file, "r+") as f:
131                     scriptname = file.replace(".acl", "")
132                     scriptacls[scriptname] = []
133                     for slice in f.readlines():
134                         scriptacls[scriptname].append(slice.rstrip())
135     # return what scripts are configured for which slices.
136     return scriptacls
137
138
139 def writeConf(slivers, oldslivers):
140     # Check if this is needed
141     # The assumption here is if lengths are the same,
142     # and the non intersection of both arrays has length 0,
143     # then the arrays are identical.
144     if (len(slivers) != len(oldslivers)) or \
145     (len(set(oldslivers) - set(slivers)) != 0):
146         logger.log("vsys:  Updating %s" % VSYSCONF)
147         f = open(VSYSCONF, "w")
148         for sliver in slivers:
149             f.write("/vservers/%(name)s/vsys %(name)s\n" % {"name": sliver})
150         f.truncate()
151         f.close()
152         return True
153     else:
154         return False
155
156
157 def parseConf():
158     '''Parse the vsys conf and return list of slices in conf.'''
159     scriptacls = {}
160     slicesinconf = []
161     try:
162         f = open(VSYSCONF)
163         for line in f.readlines():
164             (path, slice) = line.split()
165             slicesinconf.append(slice)
166         f.close()
167     except: logger.log_exc("vsys: failed parseConf")
168     return slicesinconf
169
170
171 # before shutting down slivers, it is safe to first remove them from vsys's scope
172 # so that we are sure that no dangling open file remains
173 # this will also stop vsys if needed (in which case it return True to tell caller to restart vsys once done)
174 def removeSliverFromVsys (sliver):
175     current_slivers=parseConf()
176     new_slivers= [ s for s in current_slivers if s != sliver ]
177     if writeConf (current_slivers, new_slivers):
178         stopService()
179         trashVsysHandleInSliver (sliver)
180         return True
181     else:
182         logger.log("vsys.removeSliverFromConf: no need to remove %s"%sliver)
183         return False
184
185 def trashVsysHandleInSliver (sliver):
186     slice_vsys_area = "/vservers/%s/vsys"%sliver
187     if not os.path.exists(slice_vsys_area):
188         logger.log("vsys.trashVsysHandleInSliver: no action needed, %s not found"%slice_vsys_area)
189         return
190     retcod=subprocess.call([ 'rm', '-rf' , slice_vsys_area])
191     logger.log ("vsys.trashVsysHandleInSliver: Removed %s (retcod=%s)"%(slice_vsys_area, retcod))