17a7bafe506bf72940595d420d89d517acc70994
[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                 f = open(root+"/"+file,"r+")
131                 scriptname = file.replace(".acl", "")
132                 scriptacls[scriptname] = []
133                 for slice in f.readlines():
134                     scriptacls[scriptname].append(slice.rstrip())
135                 f.close()
136     # return what scripts are configured for which slices.
137     return scriptacls
138
139
140 def writeConf(slivers, oldslivers):
141     # Check if this is needed
142     # The assumption here is if lengths are the same,
143     # and the non intersection of both arrays has length 0,
144     # then the arrays are identical.
145     if (len(slivers) != len(oldslivers)) or \
146     (len(set(oldslivers) - set(slivers)) != 0):
147         logger.log("vsys:  Updating %s" % VSYSCONF)
148         f = open(VSYSCONF,"w")
149         for sliver in slivers:
150             f.write("/vservers/%(name)s/vsys %(name)s\n" % {"name": sliver})
151         f.truncate()
152         f.close()
153         return True
154     else:
155         return False
156
157
158 def parseConf():
159     '''Parse the vsys conf and return list of slices in conf.'''
160     scriptacls = {}
161     slicesinconf = []
162     try:
163         f = open(VSYSCONF)
164         for line in f.readlines():
165             (path, slice) = line.split()
166             slicesinconf.append(slice)
167         f.close()
168     except: logger.log_exc("vsys: failed parseConf")
169     return slicesinconf
170
171
172 # before shutting down slivers, it is safe to first remove them from vsys's scope
173 # so that we are sure that no dangling open file remains
174 # this will also stop vsys if needed (in which case it return True to tell caller to restart vsys once done)
175 def removeSliverFromVsys (sliver):
176     current_slivers=parseConf()
177     new_slivers= [ s for s in current_slivers if s != sliver ]
178     if writeConf (current_slivers, new_slivers):
179         stopService()
180         trashVsysHandleInSliver (sliver)
181         return True
182     else:
183         logger.log("vsys.removeSliverFromConf: no need to remove %s"%sliver)
184         return False
185
186 def trashVsysHandleInSliver (sliver):
187     slice_vsys_area = "/vservers/%s/vsys"%sliver
188     if not os.path.exists(slice_vsys_area):
189         logger.log("vsys.trashVsysHandleInSliver: no action needed, %s not found"%slice_vsys_area)
190         return
191     retcod=subprocess.call([ 'rm', '-rf' , slice_vsys_area])
192     logger.log ("vsys.trashVsysHandleInSliver: Removed %s (retcod=%s)"%(slice_vsys_area,retcod))