improve sdnc-web for external apps
[sdnc/oam.git] / installation / sdnc-web / src / main / scripts / core.py
1 ###
2 # ============LICENSE_START=======================================================
3 # ONAP : ccsdk distribution web
4 # ================================================================================
5 # Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
6 # All rights reserved.
7 # ================================================================================
8 # Licensed under the Apache License, Version 2.0 (the "License");
9 # you may not use this file except in compliance with the License.
10 # You may obtain a copy of the License at
11 #
12 #      http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS,
16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 # ============LICENSE_END=========================================================
20 ###
21 import subprocess
22 import os
23 import json
24 import zipfile
25 import re
26 import uuid
27 import urllib3
28 import shutil
29 import re
30 import ssl
31 urllib3.disable_warnings()
32
33 APPLICATION_LISTFILE="/app/odlux.application.list"
34 INIT_FOLDER="/app/init.d"
35 ODLUX_BASE_FOLDER='/app/odlux'
36 INDEX_HTML=ODLUX_BASE_FOLDER+'/index.html'
37 INDEX_HTML_TEMPLATE=INDEX_HTML+'.template'
38 DEFAULT_APPLICATIONS=["connectApp" "faultApp" "maintenanceApp" "configurationApp" "performanceHistoryApp" "inventoryApp" "eventLogApp" "mediatorApp" "helpApp"]
39 http = urllib3.PoolManager(cert_reqs=ssl.CERT_NONE)
40     
41 def exec(command):
42     output = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE).stdout.read()
43     return output
44 def execToStdOut(commandArray):
45     process = subprocess.Popen(commandArray, shell=False)
46     process.communicate()
47
48 def download(url, dst):
49     print("downloading from {}...".format(url),end="")
50     with open(dst, 'wb') as out_file:
51         resp= http.request('GET',url, preload_content=False)
52         shutil.copyfileobj(resp, out_file)
53         resp.release_conn() 
54     print("done")
55
56 def getEnv(key, defaultValue=None):
57     x=os.getenv(key)
58     return x if x is not None and len(x)>0 else defaultValue
59
60 def sedInFile(old, nu, fn):
61     execToStdOut(['sed', '-i', 's|{}|{}|g'.format(old,nu),fn])
62
63 def add_application(name, index, file=None):
64     apps = load_applications()
65     if index==0:
66         print("no index given. put it to last position")
67         index=apps[len(apps)-1]['index']+10
68     apps.append(dict(index=index,name=name))
69     if file is not None and os.path.exists(file):
70         extract(file)
71     else:
72         print('unable to find file {}'.format(file))
73     write_applications(apps)
74     print("{} installed on index {}".format(name, index)) 
75
76 def initial_load():
77     files = os.listdir(INIT_FOLDER)
78     regex = r"([0-9]+)([a-zA-Z]+)\.(jar|zip)"
79     for file in files:
80         matches = re.finditer(regex,file)
81         match = next(matches, None)
82         if match is not None:
83             print("installing {}".format(file))
84             index = int(match.group(1))
85             name = match.group(2)
86             add_application(name,index,INIT_FOLDER+'/'+file)
87         else:
88             print("no index naming format found. try to autodetect")
89             infos = autoDetectInfosFromJar(file)
90             if infos is None:
91                 print("unable to detect index and application name for {}".format(file))
92             else:
93                add_application(infos['name'],infos['index'],INIT_FOLDER+'/'+file) 
94
95
96
97 def containsBlueprintExpression(file) -> bool:
98     print("check if file {} is blueprint".format(file))
99     with open(file, 'r') as fp:
100         lines = fp.readlines()
101         for line in lines:
102             if "<blueprint" in line:
103                 return True
104         fp.close()
105     return False
106
107 def findBlueprintXml(dir):
108     result = [os.path.join(dp, f) for dp, dn, filenames in os.walk(dir) for f in filenames if os.path.splitext(f)[1] == '.xml']
109     for file in result:
110         if containsBlueprintExpression(file):
111             return file
112     return None
113
114 def autoDetectInfosFromJar(file):
115     print("autodetect infos(appName and index) from jar {}".format(file))
116     tmpDir=getRandomTempDir()
117     regexBundleName = r"<property[\ ]+name=\"bundleName\"[\ ]+value=\"([^\"]+)\""
118     regexIndex = r"<property[\ ]+name=\"index\"[\ ]+value=\"([^\"]+)\""
119     name=None
120     index=0
121     with zipfile.ZipFile(file, 'r') as zip_ref:
122         zip_ref.extractall(tmpDir)
123         blueprint = findBlueprintXml(tmpDir)
124         if blueprint is None:
125             return None
126         with open(blueprint) as fp:
127             lines = fp.readlines()
128             for line in lines:
129                 if name is None:
130                     matches = re.finditer(regexBundleName, line)
131                     match = next(matches,None)
132                     if match is not None:
133                         name = match.group(1)
134                 if index == 0:
135                     matches = re.finditer(regexIndex, line)
136                     match = next(matches,None)
137                     if match is not None:
138                         index = int(match.group(1))
139        
140             fp.close()
141     print("found infos from jar: name={} index={}".format(name,index))
142     return dict(index=index,name=name)
143         
144 def getRandomTempDir(create=False):
145     while(True):
146         dir='/tmp/{}'.format(uuid.uuid4())
147         if not os.path.exists(dir):
148 #            print("found random not-existing dir {}".format(dir))
149             if create:
150                 os.makedirs(dir)
151             return dir
152 #        print("dir {} already exists. try new".format(dir))
153     return None
154
155 def getRandomTempFile():
156     dir = getRandomTempDir(True)
157     if dir is None:
158         return None
159
160     while True:
161         file='{}/{}.dat'.format(dir,uuid.uuid4())
162         if not os.path.exists(file):
163 #            print("found random not-existing file {}".format(file))
164             return file
165 #        print("file {} already exists. try new".format(file))
166     return None
167
168 def extract(fn):
169
170     tmpDir=getRandomTempDir()  
171     with zipfile.ZipFile(fn, 'r') as zip_ref:
172         zip_ref.extractall(tmpDir)
173         exec(" ".join(['cp','-r',tmpDir+'/odlux/*',ODLUX_BASE_FOLDER+'/']))
174         zip_ref.close()
175
176
177 def load_applications():
178     apps=[]
179     if os.path.exists(APPLICATION_LISTFILE):
180         with open(APPLICATION_LISTFILE,'r') as fp:
181             lines= fp.readlines()
182             for line in lines:
183                 if len(line.rstrip())<=0:
184                     continue
185                 try:
186                     hlp=line.split(' ')
187                     apps.append(dict(index=int(hlp[0]),name=hlp[1].rstrip()))
188                 except:
189                     print('problem reading line {}'.format(line))
190             fp.close()
191     else:
192         index=10
193         for app in DEFAULT_APPLICATIONS:
194             apps.append(dict(index=index,name=app))
195             index+=10
196 #    print('applications loaded={}'.format(apps))
197     return sorted(apps, key=lambda d: d['index']) 
198   
199 def write_applications(apps):
200 #    print('saving applications={}'.format(apps))
201     apps = sorted(apps, key=lambda d: d['index'])
202     os.remove(APPLICATION_LISTFILE)
203     with open(APPLICATION_LISTFILE,'w') as fp:
204         for app in apps:
205             fp.write('{} {}\n'.format(app['index'], app['name']))
206         fp.close()
207
208 def update_index_html(apps=None):
209  
210 #     # Backup the index.html file
211     if not os.path.exists(INDEX_HTML_TEMPLATE):
212         execToStdOut(['cp',INDEX_HTML,INDEX_HTML_TEMPLATE])
213     else:
214         execToStdOut(['cp',INDEX_HTML_TEMPLATE,INDEX_HTML])
215 #     #default values
216     if apps is None:
217         apps=load_applications()
218     ODLUX_AUTH_METHOD="basic"
219     ENABLE_ODLUX_RBAC=getEnv('ENABLE_ODLUX_RBAC','false')
220     TRPCEGUIURL=getEnv('TRPCEGUIURL')
221
222     if getEnv('ENABLE_OAUTH') == "true":
223         ODLUX_AUTH_METHOD="oauth"
224     ODLUX_CONFIG=dict(authentication=ODLUX_AUTH_METHOD,enablePolicy=ENABLE_ODLUX_RBAC == 'true')
225     print("authentication is {}".format(ODLUX_AUTH_METHOD))
226     print("rbac access is enabled: {}".format(ENABLE_ODLUX_RBAC))
227    
228     if TRPCEGUIURL is not None:
229         ODLUX_CONFIG['transportpceUrl']=TRPCEGUIURL
230         print("trpce gui url is: {}".format(TRPCEGUIURL))
231
232 #    sed -z 's/<script>[^<]*<\/script>/<script>\n    \/\/ run the application \n  require\(\[\"connectApp\",\"faultApp\",\"maintenanceApp\",\"configurationApp\",\"performanceHistoryApp\",\"inventoryApp\",\"eventLogApp\",\"mediatorApp\",\"networkMapApp\",\"linkCalculationApp\",\"helpApp\",\"run\"\], function \(connectApp,faultApp,maintenanceApp,configurationApp,performanceHistoryApp,inventoryApp,eventLogApp,mediatorApp,networkMapApp,linkCalculationApp,helpApp,run\) \{ \n run.configure('$ODLUX_CONFIG'); \n    connectApp.register\(\); \n  faultApp.register\(\);\n    maintenanceApp.register\(\); \n     configurationApp.register\(\);\n    performanceHistoryApp.register\(\); \n    inventoryApp.register\(\);\n    eventLogApp.register\(\);\n   mediatorApp.register\(\);\n   networkMapApp.register\(\);\n   linkCalculationApp.register\(\);\n     helpApp.register\(\);\n      run.runApplication();\n    \}\);\n  <\/script>/' -i /opt/bitnami/nginx/html/odlux/index.html 
233     requireArg=""
234     fnArgs=""
235     appCalls=""
236     for app in apps:
237         requireArg+='"{}",'.format(app['name'])
238         fnArgs+='{},'.format(app['name'])
239         appCalls+='{}.register();\\n'.format(app['name'])
240     #replace require expression
241     execToStdOut(['sed', '-z', 's/require(\["run"\],\ function\ (run)/require\(\[{}\"run\"\], function \({}run\)/'.format(requireArg,fnArgs), '-i', INDEX_HTML]) 
242     #replace run.runApplication expression
243     execToStdOut(['sed','-z', 's/run.runApplication();/{}run.runApplication();/'.format(appCalls), '-i',INDEX_HTML])
244     #replace run.configure expression if exists
245     execToStdOut(['sed', '-z', 's|run.configureApplication([^)]\+)|run.configureApplication({});|'.format(json.dumps(ODLUX_CONFIG)), '-i', INDEX_HTML]) 
246   
247
248 def check_for_rule_template():
249     if os.path.exists('/opt/bitnami/nginx/conf/server_blocks/location.rules.tmpl'):
250         print("using template for forwarding rules")
251         execToStdOut(['cp','/opt/bitnami/nginx/conf/server_blocks/location.rules.tmpl','/opt/bitnami/nginx/conf/server_blocks/location.rules'])
252
253 def update_nginx_site_conf():
254     FN=None
255     if getEnv('WEBPROTOCOL') == "HTTPS":
256         FN='/opt/bitnami/nginx/conf/server_blocks/https_site.conf'
257         execToStdOut(['rm', '/opt/bitnami/nginx/conf/server_blocks/http_site.conf'])
258         SSL_CERT_DIR=getEnv('SSL_CERT_DIR')
259         SSL_CERTIFICATE=getEnv('SSL_CERTIFICATE')
260         SSL_CERTIFICATE_KEY=getEnv('SSL_CERTIFICATE_KEY')
261         sedInFile('SSL_CERTIFICATE_KEY',SSL_CERTIFICATE_KEY,FN)
262         sedInFile('SSL_CERT_DIR',SSL_CERT_DIR,FN)
263         sedInFile('SSL_CERTIFICATE',SSL_CERTIFICATE, FN)
264         
265     elif getEnv('WEBPROTOCOL') == "HTTP":
266         FN='/opt/bitnami/nginx/conf/server_blocks/http_site.conf'
267         execToStdOut(['rm', '/opt/bitnami/nginx/conf/server_blocks/https_site.conf'])
268
269     WEBPROTOCOL=getEnv('WEBPROTOCOL')
270     WEBPORT=getEnv('WEBPORT')
271     SDNRPROTOCOL=getEnv('SDNRPROTOCOL')
272     SDNRHOST=getEnv('SDNRHOST')
273     SDNRPORT=getEnv('SDNRPORT')
274     DNS_RESOLVER=getEnv('DNS_RESOLVER')
275     DNS_INTERNAL_RESOLVER=getEnv('DNS_INTERNAL_RESOLVER')
276     if FN is None:
277         print("unknown env WEBPROTOCOL: {}".format(WEBPROTOCOL))
278         exit(1)
279     
280     # replace needed base parameters
281     sedInFile('WEBPORT',WEBPORT,FN)
282
283     FN='/opt/bitnami/nginx/conf/server_blocks/location.rules'
284     # replace needed parameters in forwarding rules
285     sedInFile('WEBPORT',WEBPORT,FN)
286     sedInFile('SDNRPROTOCOL',SDNRPROTOCOL,FN)
287     sedInFile('SDNRHOST',SDNRHOST ,FN)
288     sedInFile('SDNRPORT',SDNRPORT,FN)
289     sedInFile('DNS_RESOLVER',DNS_RESOLVER ,FN)
290     sedInFile('DNS_INTERNAL_RESOLVER',DNS_INTERNAL_RESOLVER ,FN)
291
292     TRPCEURL=getEnv('TRPCEURL')
293     TOPOURL=getEnv('TOPOURL')
294     SITEDOCURL=getEnv('SITEDOCURL')
295     TILEURL=getEnv('TILEURL')
296     DATAPROVIDERURL=getEnv('DATAPROVIDERURL')
297     TERRAINURL=getEnv('TERRAINURL')
298     # handle optional parameters
299     if TRPCEURL is None:
300         print("transportPCE forwarding disabled")
301         sedInFile('proxy_pass TRPCEURL/$1;','return 404;',FN)
302     else:
303         sedInFile('TRPCEURL',TRPCEURL ,FN)
304
305     if TOPOURL is None:
306         print("topology api forwarding disabled")
307         sedInFile('proxy_pass TOPOURL;','return 404;',FN)
308     else:
309         sedInFile('TOPOURL',TOPOURL ,FN)
310     
311     if SITEDOCURL is None:
312         print("sitedoc api forwarding disabled")
313         sedInFile('proxy_pass SITEDOCURL/topology/stadok/$1;','return 404;', FN)
314     else:
315         sedInFile('SITEDOCURL',SITEDOCURL, FN)
316     
317     if TILEURL is None:
318         print("tile server forwarding disabled")
319         sedInFile('proxy_pass TILEURL/$1;','return 404;',FN)
320     else:
321         sedInFile('TILEURL',TILEURL ,FN)
322     
323     if DATAPROVIDERURL is None:
324         print("data provider forwarding disabled")
325         sedInFile('proxy_pass DATAPROVIDERURL/$1;','return 404;',FN)
326     else:
327         sedInFile('DATAPROVIDERURL',DATAPROVIDERURL ,FN)
328     
329     if TERRAINURL is None:
330         print("terrain server forwarding disabled")
331         sedInFile('proxy_pass TERRAINURL/$1;','return 404;',FN)
332     else:
333         sedInFile('TERRAINURL',TERRAINURL ,FN)