Merge "new option --screen at basictest to force launching in a screen"
[osm/RO.git] / openmano
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # PYTHON_ARGCOMPLETE_OK
4
5 ##
6 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
7 # This file is part of openmano
8 # All Rights Reserved.
9 #
10 # Licensed under the Apache License, Version 2.0 (the "License"); you may
11 # not use this file except in compliance with the License. You may obtain
12 # a copy of the License at
13 #
14 #         http://www.apache.org/licenses/LICENSE-2.0
15 #
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
19 # License for the specific language governing permissions and limitations
20 # under the License.
21 #
22 # For those usages not covered by the Apache License, Version 2.0 please
23 # contact with: nfvlabs@tid.es
24 ##
25
26 '''
27 openmano client used to interact with openmano-server (openmanod) 
28 '''
29 __author__="Alfonso Tierno, Gerardo Garcia"
30 __date__ ="$09-oct-2014 09:09:48$"
31 __version__="0.4.6-r500"
32 version_date="Sep 2016"
33
34 from argcomplete.completers import FilesCompleter
35 import os
36 import argparse
37 import argcomplete
38 import requests
39 import json
40 import yaml
41 import logging
42 #from jsonschema import validate as js_v, exceptions as js_e
43
44 class ArgumentParserError(Exception): pass
45
46 class OpenmanoCLIError(Exception): pass
47
48 class ThrowingArgumentParser(argparse.ArgumentParser):
49     def error(self, message):
50         print "Error: %s" %message
51         print
52         self.print_usage()
53         #self.print_help()
54         print
55         print "Type 'openmano -h' for help"
56         raise ArgumentParserError
57
58
59 def config(args):
60     print "OPENMANO_HOST: %s" %mano_host
61     print "OPENMANO_PORT: %s" %mano_port
62     if args.n:
63         logger.debug("resolving tenant and datacenter names")
64         mano_tenant_id = "None"
65         mano_tenant_name = "None"
66         mano_datacenter_id = "None"
67         mano_datacenter_name = "None"
68         try:
69             mano_tenant_id = _get_item_uuid("tenants", mano_tenant)
70             URLrequest = "http://%s:%s/openmano/tenants/%s" %(mano_host, mano_port, mano_tenant_id)
71             mano_response = requests.get(URLrequest)
72             logger.debug("openmano response: %s", mano_response.text )
73             content = mano_response.json()
74             mano_tenant_name = content["tenant"]["name"]
75             URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, mano_tenant_id, mano_datacenter)
76             mano_response = requests.get(URLrequest)
77             logger.debug("openmano response: %s", mano_response.text )
78             content = mano_response.json()
79             if "error" not in content:
80                 mano_datacenter_id = content["datacenter"]["uuid"]
81                 mano_datacenter_name = content["datacenter"]["name"]
82         except OpenmanoCLIError:
83             pass
84         print "OPENMANO_TENANT: %s" %mano_tenant
85         print "    Id: %s" %mano_tenant_id
86         print "    Name: %s" %mano_tenant_name 
87         print "OPENMANO_DATACENTER: %s" %str (mano_datacenter)
88         print "    Id: %s" %mano_datacenter_id
89         print "    Name: %s" %mano_datacenter_name 
90     else:
91         print "OPENMANO_TENANT: %s" %mano_tenant
92         print "OPENMANO_DATACENTER: %s" %str (mano_datacenter)
93
94 def _print_verbose(mano_response, verbose_level=0):
95     content = mano_response.json()
96     result = 0 if mano_response.status_code==200 else mano_response.status_code
97     if type(content)!=dict or len(content)!=1:
98         #print "Non expected format output"
99         print str(content)
100         return result
101     
102     val=content.values()[0]
103     if type(val)==str:
104         print val
105         return result
106     elif type(val) == list:
107         content_list = val
108     elif type(val)==dict:
109         content_list = [val]
110     else:
111         #print "Non expected dict/list format output"
112         print str(content)
113         return result
114     
115     #print content_list
116     if verbose_level==None:
117         verbose_level=0
118     if verbose_level >= 3:
119         print yaml.safe_dump(content, indent=4, default_flow_style=False)
120         return result
121
122     if mano_response.status_code == 200:
123         for content in content_list:
124             if "uuid" in content:
125                 uuid = content['uuid']
126             elif "id" in content:
127                 uuid = content['id']
128             elif "vim_id" in content:
129                 uuid = content['vim_id']
130             myoutput = "%s %s" %(uuid.ljust(38),content['name'].ljust(20))
131             if "status" in content:
132                 myoutput += " " + content['status'].ljust(20)
133             elif "enabled" in content and not content["enabled"]:
134                 myoutput += " enabled=False".ljust(20)
135             if verbose_level >=1:
136                 if 'created_at' in content:
137                     myoutput += " " + content['created_at'].ljust(20)
138                 if verbose_level >=2:
139                     new_line='\n'
140                     if 'type' in content and content['type']!=None:
141                         myoutput += new_line + "  Type: " + content['type'].ljust(29)
142                         new_line=''
143                     if 'description' in content and content['description']!=None:
144                         myoutput += new_line + "  Description: " + content['description'].ljust(20)
145             print myoutput
146     else:
147         print content['error']['description']
148     return result
149
150 def parser_json_yaml(file_name):
151     try:
152         f = file(file_name, "r")
153         text = f.read()
154         f.close()
155     except Exception as e:
156         return (False, str(e))
157            
158     #Read and parse file
159     if file_name[-5:]=='.yaml' or file_name[-4:]=='.yml' or (file_name[-5:]!='.json' and '\t' not in text):
160         try:
161             config = yaml.load(text)
162         except yaml.YAMLError as exc:
163             error_pos = ""
164             if hasattr(exc, 'problem_mark'):
165                 mark = exc.problem_mark
166                 error_pos = " at line:%s column:%s" % (mark.line+1, mark.column+1)
167             return (False, "Error loading file '"+file_name+"' yaml format error" + error_pos)
168     else: #json
169         try:
170             config = json.loads(text) 
171         except Exception as e:
172             return (False, "Error loading file '"+file_name+"' json format error " + str(e) )
173
174     return True, config
175
176 def _load_file_or_yaml(content):
177     '''
178     'content' can be or a yaml/json file or a text containing a yaml/json text format
179     This function autodetect, trying to load and parse the file,
180     if fails trying to parse the 'content' text
181     Returns the dictionary once parsed, or print an error and finish the program
182     '''
183     #Check config file exists
184     if os.path.isfile(content):
185         r,payload = parser_json_yaml(content)
186         if not r:
187             print payload
188             exit(-1)
189     elif "{" in content or ":" in content:
190         try:
191             payload = yaml.load(content)
192         except yaml.YAMLError as exc:
193             error_pos = ""
194             if hasattr(exc, 'problem_mark'):
195                 mark = exc.problem_mark
196                 error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1)
197             print "Error loading yaml/json text"+error_pos
198             exit (-1)
199     else:
200         print "'%s' is neither a valid file nor a yaml/json content" % content
201         exit(-1)
202     return payload
203
204 def _get_item_uuid(item, item_name_id, tenant=None):
205     if tenant:
206         URLrequest = "http://%s:%s/openmano/%s/%s" %(mano_host, mano_port, tenant, item)
207     else:
208         URLrequest = "http://%s:%s/openmano/%s" %(mano_host, mano_port, item)
209     mano_response = requests.get(URLrequest)
210     logger.debug("openmano response: %s", mano_response.text )
211     content = mano_response.json()
212     #print content
213     found = 0
214     for i in content[item]:
215         if i["uuid"] == item_name_id:
216             return item_name_id
217         if i["name"] == item_name_id:
218             uuid = i["uuid"]
219             found += 1
220     if found == 0:
221         raise OpenmanoCLIError("No %s found with name/uuid '%s'" %(item[:-1], item_name_id))
222     elif found > 1:
223         raise OpenmanoCLIError("%d %s found with name '%s'. uuid must be used" %(found, item, item_name_id))
224     return uuid
225
226 # def check_valid_uuid(uuid):
227 #     id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
228 #     try:
229 #         js_v(uuid, id_schema)
230 #         return True
231 #     except js_e.ValidationError:
232 #         return False
233     
234 def _get_tenant(tenant_name_id = None):
235     if not tenant_name_id:
236         tenant_name_id = mano_tenant
237         if not mano_tenant:
238             raise OpenmanoCLIError("'OPENMANO_TENANT' environment variable is not set")
239     return _get_item_uuid("tenants", tenant_name_id)
240
241 def _get_datacenter(datacenter_name_id = None, tenant = "any"):
242     if not datacenter_name_id:
243         datacenter_name_id = mano_datacenter
244         if not datacenter_name_id:
245             raise OpenmanoCLIError("neither 'OPENMANO_DATACENTER' environment variable is set nor --datacenter option is used")
246     return _get_item_uuid("datacenters", datacenter_name_id, tenant)
247
248 def vnf_create(args):
249     #print "vnf-create",args
250     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
251     tenant = _get_tenant()
252     myvnf = _load_file_or_yaml(args.file)
253
254     if args.name or args.description or args.image_path:
255         #print args.name
256         try:
257             if args.name:
258                 myvnf['vnf']['name'] = args.name
259             if args.description:
260                 myvnf['vnf']['description'] = args.description
261             if args.image_path:
262                 index=0
263                 for image_path_ in args.image_path.split(","):
264                     #print "image-path", image_path_
265                     myvnf['vnf']['VNFC'][index]['VNFC image']=image_path_
266                     index=index+1
267         except (KeyError, TypeError), e:
268             if str(e)=='vnf':           error_pos= "missing field 'vnf'"
269             elif str(e)=='name':        error_pos= "missing field  'vnf':'name'"
270             elif str(e)=='description': error_pos= "missing field  'vnf':'description'"
271             elif str(e)=='VNFC':        error_pos= "missing field  'vnf':'VNFC'"
272             elif str(e)==str(index):    error_pos= "field  'vnf':'VNFC' must be an array"
273             elif str(e)=='VNFC image':  error_pos= "missing field 'vnf':'VNFC'['VNFC image']"
274             else:                       error_pos="wrong format"
275             print "Wrong VNF descriptor: " + error_pos
276             return -1 
277     payload_req = json.dumps(myvnf)
278         
279     #print payload_req
280         
281     URLrequest = "http://%s:%s/openmano/%s/vnfs" %(mano_host, mano_port, tenant)
282     logger.debug("openmano request: %s", payload_req)
283     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
284     logger.debug("openmano response: %s", mano_response.text )
285
286     return _print_verbose(mano_response, args.verbose)
287
288 def vnf_list(args):
289     #print "vnf-list",args
290     if args.all:
291         tenant = "any"
292     else:
293         tenant = _get_tenant()
294     if args.name:
295         toshow = _get_item_uuid("vnfs", args.name, tenant)
296         URLrequest = "http://%s:%s/openmano/%s/vnfs/%s" %(mano_host, mano_port, tenant, toshow)
297     else:
298         URLrequest = "http://%s:%s/openmano/%s/vnfs" %(mano_host, mano_port, tenant)
299     mano_response = requests.get(URLrequest)
300     logger.debug("openmano response: %s", mano_response.text )
301     content = mano_response.json()
302     #print json.dumps(content, indent=4)
303     if args.verbose==None:
304         args.verbose=0
305     result = 0 if mano_response.status_code==200 else mano_response.status_code
306     if mano_response.status_code == 200:
307         if not args.name:
308             if args.verbose >= 3:
309                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
310                 return result
311             if len(content['vnfs']) == 0:
312                 print "No VNFs were found."
313                 return 404 #HTTP_Not_Found
314             for vnf in content['vnfs']:
315                 myoutput = "%s %s" %(vnf['uuid'].ljust(38),vnf['name'].ljust(20))
316                 if args.verbose >=1:
317                     myoutput = "%s %s" %(myoutput, vnf['created_at'].ljust(20))
318                 print myoutput
319                 if args.verbose >=2:
320                     print "  Description: %s" %vnf['description']
321                     print "  VNF descriptor file: %s" %vnf['path']
322         else:
323             if args.verbose:
324                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
325                 return result
326             vnf = content['vnf']
327             print "%s %s %s" %(vnf['uuid'].ljust(38),vnf['name'].ljust(20), vnf['created_at'].ljust(20))
328             print "  Description: %s" %vnf['description']
329             #print "  VNF descriptor file: %s" %vnf['path']
330             print "    VMs:"
331             for vm in vnf['VNFC']:
332                 #print "    %s %s %s" %(vm['name'].ljust(20), vm['uuid'].ljust(38), vm['description'].ljust(30))
333                 print "        %s %s" %(vm['name'].ljust(20), vm['description'])
334             if len(vnf['nets'])>0:
335                 print "    Internal nets:"
336                 for net in vnf['nets']:
337                     print "        %s %s" %(net['name'].ljust(20), net['description'])
338             if len(vnf['external-connections'])>0:
339                 print "    External interfaces:"
340                 for interface in vnf['external-connections']:
341                     print "        %s %s %s %s" %(interface['external_name'].ljust(20), interface['vm_name'].ljust(20), interface['internal_name'].ljust(20), \
342                                                   interface['vpci'].ljust(14))
343     else:
344         print content['error']['description']
345         if args.verbose:
346             print yaml.safe_dump(content, indent=4, default_flow_style=False)
347     return result
348
349 def vnf_delete(args):
350     #print "vnf-delete",args
351     if args.all:
352         tenant = "any"
353     else:
354         tenant = _get_tenant()
355     todelete = _get_item_uuid("vnfs", args.name, tenant=tenant)
356     if not args.force:
357         r = raw_input("Delete VNF %s (y/N)? " %(todelete))
358         if  not (len(r)>0  and r[0].lower()=="y"):
359             return 0
360     URLrequest = "http://%s:%s/openmano/%s/vnfs/%s" %(mano_host, mano_port, tenant, todelete)
361     mano_response = requests.delete(URLrequest)
362     logger.debug("openmano response: %s", mano_response.text )
363     result = 0 if mano_response.status_code==200 else mano_response.status_code
364     content = mano_response.json()
365     #print json.dumps(content, indent=4)
366     if mano_response.status_code == 200:
367         print content['result']
368     else:
369         print content['error']['description']
370     return result
371
372 def scenario_create(args):
373     #print "scenario-create",args
374     tenant = _get_tenant()
375     headers_req = {'content-type': 'application/yaml'}
376     myscenario = _load_file_or_yaml(args.file)
377
378     if args.name:
379         myscenario['name'] = args.name
380     if args.description:
381         myscenario['description'] = args.description
382     payload_req = yaml.safe_dump(myscenario, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
383     
384     #print payload_req
385         
386     URLrequest = "http://%s:%s/openmano/%s/scenarios" %(mano_host, mano_port, tenant)
387     logger.debug("openmano request: %s", payload_req)
388     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
389     logger.debug("openmano response: %s", mano_response.text )
390     return _print_verbose(mano_response, args.verbose)
391
392 def scenario_list(args):
393     #print "scenario-list",args
394     if args.all:
395         tenant = "any"
396     else:
397         tenant = _get_tenant()
398     if args.name:
399         toshow = _get_item_uuid("scenarios", args.name, tenant)
400         URLrequest = "http://%s:%s/openmano/%s/scenarios/%s" %(mano_host, mano_port, tenant, toshow)
401     else:
402         URLrequest = "http://%s:%s/openmano/%s/scenarios" %(mano_host, mano_port, tenant)
403     mano_response = requests.get(URLrequest)
404     logger.debug("openmano response: %s", mano_response.text )
405     content = mano_response.json()
406     #print json.dumps(content, indent=4)
407     if args.verbose==None:
408         args.verbose=0
409
410     result = 0 if mano_response.status_code==200 else mano_response.status_code
411     if mano_response.status_code == 200:
412         if not args.name:
413             if args.verbose >= 3:
414                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
415                 return result
416             if len(content['scenarios']) == 0:
417                 print "No scenarios were found."
418                 return 404 #HTTP_Not_Found
419             for scenario in content['scenarios']:
420                 myoutput = "%s %s" %(scenario['uuid'].ljust(38),scenario['name'].ljust(20))
421                 if args.verbose >=1:
422                     myoutput = "%s %s" %(myoutput, scenario['created_at'].ljust(20))
423                 print myoutput
424                 if args.verbose >=2:
425                     print "  Description: %s" %scenario['description']
426         else:
427             if args.verbose:
428                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
429                 return result
430             scenario = content['scenario']
431             myoutput = "%s %s %s" %(scenario['uuid'].ljust(38),scenario['name'].ljust(20), scenario['created_at'].ljust(20))
432             print myoutput
433             print "  Description: %s" %scenario['description']
434             print "    VNFs:"
435             for vnf in scenario['vnfs']:
436                 print "        %s %s %s" %(vnf['name'].ljust(20), vnf['vnf_id'].ljust(38), vnf['description'])
437             if len(scenario['nets'])>0:
438                 print "    Internal nets:"
439                 for net in scenario['nets']:
440                     if net['description'] is None:   #if description does not exist, description is "-". Valid for external and internal nets.
441                         net['description'] = '-' 
442                     if not net['external']:
443                         print "        %s %s %s" %(net['name'].ljust(20), net['uuid'].ljust(38), net['description'].ljust(30))
444                 print "    External nets:"
445                 for net in scenario['nets']:
446                     if net['external']:
447                         print "        %s %s %s vim-id:%s" %(net['name'].ljust(20), net['uuid'].ljust(38), net['description'].ljust(30), net['vim_id'])
448     else:
449         print content['error']['description']
450         if args.verbose:
451             print yaml.safe_dump(content, indent=4, default_flow_style=False)
452     return result
453
454 def scenario_delete(args):
455     #print "scenario-delete",args
456     if args.all:
457         tenant = "any"
458     else:
459         tenant = _get_tenant()
460     todelete = _get_item_uuid("scenarios", args.name, tenant=tenant)
461     if not args.force:
462         r = raw_input("Delete scenario %s (y/N)? " %(args.name))
463         if  not (len(r)>0  and r[0].lower()=="y"):
464             return 0
465     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s" %(mano_host, mano_port, tenant, todelete)
466     mano_response = requests.delete(URLrequest)
467     logger.debug("openmano response: %s", mano_response.text )
468     result = 0 if mano_response.status_code==200 else mano_response.status_code
469     content = mano_response.json()
470     #print json.dumps(content, indent=4)
471     if mano_response.status_code == 200:
472         print content['result']
473     else:
474         print content['error']['description']
475     return result
476
477 def scenario_deploy(args):
478     print "This command is deprecated, use 'openmano instance-scenario-create --scenario %s --name %s' instead!!!" % (args.scenario, args.name)
479     print
480     args.file = None
481     args.netmap_use = None
482     args.netmap_create = None
483     args.keypair = None
484     args.keypair_auto = None
485     return instance_create(args)
486
487 #     #print "scenario-deploy",args
488 #     headers_req = {'content-type': 'application/json'}
489 #     action = {}
490 #     actionCmd="start"
491 #     if args.nostart:
492 #         actionCmd="reserve"
493 #     action[actionCmd] = {}
494 #     action[actionCmd]["instance_name"] = args.name
495 #     if args.datacenter != None:
496 #         action[actionCmd]["datacenter"] = args.datacenter
497 #     elif mano_datacenter != None:
498 #         action[actionCmd]["datacenter"] = mano_datacenter
499 #         
500 #     if args.description:
501 #         action[actionCmd]["description"] = args.description
502 #     payload_req = json.dumps(action, indent=4)
503 #     #print payload_req
504
505 #     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, mano_tenant, args.scenario)
506 #     logger.debug("openmano request: %s", payload_req)
507 #     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
508 #     logger.debug("openmano response: %s", mano_response.text )
509 #     if args.verbose==None:
510 #         args.verbose=0
511 #     
512 #     result = 0 if mano_response.status_code==200 else mano_response.status_code
513 #     content = mano_response.json()
514 #     #print json.dumps(content, indent=4)
515 #     if args.verbose >= 3:
516 #         print yaml.safe_dump(content, indent=4, default_flow_style=False)
517 #         return result
518
519 #     if mano_response.status_code == 200:
520 #         myoutput = "%s %s" %(content['uuid'].ljust(38),content['name'].ljust(20))
521 #         if args.verbose >=1:
522 #             myoutput = "%s %s" %(myoutput, content['created_at'].ljust(20))
523 #         if args.verbose >=2:
524 #             myoutput = "%s %s %s" %(myoutput, content['description'].ljust(30))
525 #         print myoutput
526 #         print ""
527 #         print "To check the status, run the following command:"
528 #         print "openmano instance-scenario-list <instance_id>"
529 #     else:
530 #         print content['error']['description']
531 #     return result
532
533 def scenario_verify(args):
534     #print "scenario-verify",args
535     headers_req = {'content-type': 'application/json'}
536     action = {}
537     action["verify"] = {}
538     action["verify"]["instance_name"] = "scen-verify-return5"
539     payload_req = json.dumps(action, indent=4)
540     #print payload_req
541
542     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, mano_tenant, args.scenario)
543     logger.debug("openmano request: %s", payload_req)
544     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
545     logger.debug("openmano response: %s", mano_response.text )
546     
547     result = 0 if mano_response.status_code==200 else mano_response.status_code
548     content = mano_response.json()
549     #print json.dumps(content, indent=4)
550     if mano_response.status_code == 200:
551         print content['result']
552     else:
553         print content['error']['description']
554     return result
555
556 def instance_create(args):
557     tenant = _get_tenant()
558     headers_req = {'content-type': 'application/yaml'}
559     myInstance={"instance": {}, "schema_version": "0.1"}
560     if args.file:
561         instance_dict = _load_file_or_yaml(args.file)
562         if "instance" not in instance_dict:
563             myInstance = {"instance": instance_dict, "schema_version": "0.1"}
564         else:
565             myInstance = instance_dict
566     if args.name:
567         myInstance["instance"]['name'] = args.name
568     if args.description:
569         myInstance["instance"]['description'] = args.description
570     if args.nostart:
571         myInstance["instance"]['action'] = "reserve"
572     #datacenter
573     datacenter = myInstance["instance"].get("datacenter")
574     if args.datacenter != None:
575         datacenter = args.datacenter
576     myInstance["instance"]["datacenter"] = _get_datacenter(datacenter, tenant)
577     #scenario
578     scenario = myInstance["instance"].get("scenario")
579     if args.scenario != None:
580         scenario = args.scenario
581     if not scenario:
582         print "you must provide an scenario in the file descriptor or with --scenario"
583         return -1
584     myInstance["instance"]["scenario"] = _get_item_uuid("scenarios", scenario, tenant)
585     if args.netmap_use:
586         if "networks" not in myInstance["instance"]:
587             myInstance["instance"]["networks"] = {}
588         for net in args.netmap_use:
589             net_comma_list = net.split(",")
590             for net_comma in net_comma_list:
591                 net_tuple = net_comma.split("=")
592                 if len(net_tuple) != 2:
593                     print "error at netmap-use. Expected net-scenario=net-datacenter. (%s)?" % net_comma
594                     return
595                 net_scenario   = net_tuple[0].strip()
596                 net_datacenter = net_tuple[1].strip()
597                 if net_scenario not in myInstance["instance"]["networks"]:
598                     myInstance["instance"]["networks"][net_scenario] = {} 
599                 if "sites" not in myInstance["instance"]["networks"][net_scenario]:
600                     myInstance["instance"]["networks"][net_scenario]["sites"] = [ {} ]
601                 myInstance["instance"]["networks"][net_scenario]["sites"][0]["netmap-use"] = net_datacenter
602     if args.netmap_create:
603         if "networks" not in myInstance["instance"]:
604             myInstance["instance"]["networks"] = {}
605         for net in args.netmap_create:
606             net_comma_list = net.split(",")
607             for net_comma in net_comma_list:
608                 net_tuple = net_comma.split("=")
609                 if len(net_tuple) == 1:
610                     net_scenario   = net_tuple[0].strip()
611                     net_datacenter = None
612                 elif len(net_tuple) == 2:
613                     net_scenario   = net_tuple[0].strip()
614                     net_datacenter = net_tuple[1].strip()
615                 else:
616                     print "error at netmap-create. Expected net-scenario=net-datacenter or net-scenario. (%s)?" % net_comma
617                     return
618                 if net_scenario not in myInstance["instance"]["networks"]:
619                     myInstance["instance"]["networks"][net_scenario] = {} 
620                 if "sites" not in myInstance["instance"]["networks"][net_scenario]:
621                     myInstance["instance"]["networks"][net_scenario]["sites"] = [ {} ]
622                 myInstance["instance"]["networks"][net_scenario]["sites"][0]["netmap-create"] = net_datacenter
623     if args.keypair:
624         if "cloud-config" not in myInstance["instance"]:
625             myInstance["instance"]["cloud-config"] = {}
626         cloud_config = myInstance["instance"]["cloud-config"]
627         for key in args.keypair:
628             index = key.find(":")
629             if index<0:
630                 if "key-pairs" not in cloud_config:
631                     cloud_config["key-pairs"] = []
632                 cloud_config["key-pairs"].append(key)
633             else:
634                 user = key[:index]
635                 key_ = key[index+1:]
636                 key_list = key_.split(",")
637                 if "users" not in cloud_config:
638                     cloud_config["users"] = []
639                 cloud_config["users"].append({"name": user, "key-pairs": key_list  })
640     if args.keypair_auto:
641         try:
642             keys=[]
643             home = os.getenv("HOME")
644             user = os.getenv("USER")
645             files = os.listdir(home+'/.ssh')
646             for file in files:
647                 if file[-4:] == ".pub":
648                     with open(home+'/.ssh/'+file, 'r') as f:
649                         keys.append(f.read())
650             if not keys:
651                 print "Cannot obtain any public ssh key from '{}'. Try not using --keymap-auto".format(home+'/.ssh')
652                 return 1
653         except Exception as e:
654             print "Cannot obtain any public ssh key. Error '{}'. Try not using --keymap-auto".format(str(e))
655             return 1
656         
657         if "cloud-config" not in myInstance["instance"]:
658             myInstance["instance"]["cloud-config"] = {}
659         cloud_config = myInstance["instance"]["cloud-config"]
660         if "key-pairs" not in cloud_config:
661             cloud_config["key-pairs"] = []
662         if user:
663             if "users" not in cloud_config:
664                 cloud_config["users"] = []
665             cloud_config["users"].append({"name": user, "key-pairs": keys })                    
666                         
667     payload_req = yaml.safe_dump(myInstance, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
668     logger.debug("openmano request: %s", payload_req)
669     URLrequest = "http://%s:%s/openmano/%s/instances" %(mano_host, mano_port, tenant)
670     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
671     logger.debug("openmano response: %s", mano_response.text )
672     if args.verbose==None:
673         args.verbose=0
674     
675     result = 0 if mano_response.status_code==200 else mano_response.status_code
676     content = mano_response.json()
677     #print json.dumps(content, indent=4)
678     if args.verbose >= 3:
679         print yaml.safe_dump(content, indent=4, default_flow_style=False)
680         return result
681
682     if mano_response.status_code == 200:
683         myoutput = "%s %s" %(content['uuid'].ljust(38),content['name'].ljust(20))
684         if args.verbose >=1:
685             myoutput = "%s %s" %(myoutput, content['created_at'].ljust(20))
686         if args.verbose >=2:
687             myoutput = "%s %s %s" %(myoutput, content['description'].ljust(30))
688         print myoutput
689     else:
690         print content['error']['description']
691     return result
692
693 def instance_scenario_list(args):
694     #print "instance-scenario-list",args
695     if args.all:
696         tenant = "any"
697     else:
698         tenant = _get_tenant()
699     if args.name:
700         toshow = _get_item_uuid("instances", args.name, tenant)
701         URLrequest = "http://%s:%s/openmano/%s/instances/%s" %(mano_host, mano_port, tenant, toshow)
702     else:
703         URLrequest = "http://%s:%s/openmano/%s/instances" %(mano_host, mano_port, tenant)
704     mano_response = requests.get(URLrequest)
705     logger.debug("openmano response: %s", mano_response.text )
706     content = mano_response.json()
707     #print json.dumps(content, indent=4)
708     if args.verbose==None:
709         args.verbose=0
710
711     result = 0 if mano_response.status_code==200 else mano_response.status_code
712     if mano_response.status_code == 200:
713         if not args.name:
714             if args.verbose >= 3:
715                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
716                 return result
717             if len(content['instances']) == 0:
718                 print "No scenario instances were found."
719                 return result
720             for instance in content['instances']:
721                 myoutput = "%s %s" %(instance['uuid'].ljust(38),instance['name'].ljust(20))
722                 if args.verbose >=1:
723                     myoutput = "%s %s" %(myoutput, instance['created_at'].ljust(20))
724                 print myoutput
725                 if args.verbose >=2:
726                     print "Description: %s" %instance['description']
727         else:
728             if args.verbose:
729                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
730                 return result
731             instance = content
732             print "%s %s %s" %(instance['uuid'].ljust(38),instance['name'].ljust(20),instance['created_at'].ljust(20))
733             print "Description: %s" %instance['description']
734             print "Template scenario id: %s" %instance['scenario_id']
735             print "Template scenario name: %s" %instance['scenario_name']
736             print "---------------------------------------"
737             print "VNF instances: %d" %len(instance['vnfs'])
738             for vnf in instance['vnfs']:
739                 #print "    %s %s Template vnf name: %s Template vnf id: %s" %(vnf['uuid'].ljust(38), vnf['name'].ljust(20), vnf['vnf_name'].ljust(20), vnf['vnf_id'].ljust(38))
740                 print "    %s %s Template vnf id: %s" %(vnf['uuid'].ljust(38), vnf['vnf_name'].ljust(20), vnf['vnf_id'].ljust(38))
741             if len(instance['nets'])>0:
742                 print "---------------------------------------"
743                 print "Internal nets:"
744                 for net in instance['nets']:
745                     if net['created']:
746                         print "    %s %s VIM ID: %s" %(net['uuid'].ljust(38), net['status'].ljust(12), net['vim_net_id'])
747                 print "---------------------------------------"
748                 print "External nets:"
749                 for net in instance['nets']:
750                     if not net['created']:
751                         print "    %s %s VIM ID: %s" %(net['uuid'].ljust(38), net['status'].ljust(12), net['vim_net_id'])
752             print "---------------------------------------"
753             print "VM instances:"
754             for vnf in instance['vnfs']:
755                 for vm in vnf['vms']:
756                     print "    %s %s %s %s VIM ID: %s" %(vm['uuid'].ljust(38), vnf['vnf_name'].ljust(20), vm['name'].ljust(20), vm['status'].ljust(12), vm['vim_vm_id'])
757     else:
758         print content['error']['description']
759         if args.verbose:
760             print yaml.safe_dump(content, indent=4, default_flow_style=False)
761     return result
762
763 def instance_scenario_status(args):
764     print "instance-scenario-status"
765     return 0
766
767 def instance_scenario_delete(args):
768     if args.all:
769         tenant = "any"
770     else:
771         tenant = _get_tenant()
772     todelete = _get_item_uuid("instances", args.name, tenant=tenant)
773     #print "instance-scenario-delete",args
774     if not args.force:
775         r = raw_input("Delete scenario instance %s (y/N)? " %(args.name))
776         if  not (len(r)>0  and r[0].lower()=="y"):
777             return
778     URLrequest = "http://%s:%s/openmano/%s/instances/%s" %(mano_host, mano_port, tenant, todelete)
779     mano_response = requests.delete(URLrequest)
780     logger.debug("openmano response: %s", mano_response.text )
781     result = 0 if mano_response.status_code==200 else mano_response.status_code
782     content = mano_response.json()
783     #print json.dumps(content, indent=4)
784     if mano_response.status_code == 200:
785         print content['result']
786     else:
787         print content['error']['description']
788     return result
789
790 def instance_scenario_action(args):
791     #print "instance-scenario-action", args
792     tenant = _get_tenant()
793     toact = _get_item_uuid("instances", args.name, tenant=tenant)
794     action={}
795     action[ args.action ] = args.param
796     if args.vnf:
797         action["vnfs"] = args.vnf
798     if args.vm:
799         action["vms"] = args.vm
800     
801     headers_req = {'content-type': 'application/json'}
802     payload_req = json.dumps(action, indent=4)
803     URLrequest = "http://%s:%s/openmano/%s/instances/%s/action" %(mano_host, mano_port, tenant, toact)
804     logger.debug("openmano request: %s", payload_req)
805     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
806     logger.debug("openmano response: %s", mano_response.text )
807     result = 0 if mano_response.status_code==200 else mano_response.status_code
808     content = mano_response.json()
809     #print json.dumps(content, indent=4)
810     if mano_response.status_code == 200:
811         if args.verbose:
812             print yaml.safe_dump(content, indent=4, default_flow_style=False)
813             return result
814         for uuid,c in content.iteritems():
815             print "%s %s %s" %(uuid.ljust(38), c['name'].ljust(20),c['description'].ljust(20))
816     else:
817         print content['error']['description']
818     return result
819
820
821 def instance_vnf_list(args):
822     print "instance-vnf-list"
823     return 0
824
825 def instance_vnf_status(args):
826     print "instance-vnf-status"
827     return 0
828
829 def tenant_create(args):
830     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
831     tenant_dict={"name": args.name}
832     if args.description!=None:
833         tenant_dict["description"] = args.description 
834     payload_req = json.dumps( {"tenant": tenant_dict })
835     
836     #print payload_req
837         
838     URLrequest = "http://%s:%s/openmano/tenants" %(mano_host, mano_port)
839     logger.debug("openmano request: %s", payload_req)
840     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
841     logger.debug("openmano response: %s", mano_response.text )
842     return _print_verbose(mano_response, args.verbose)
843
844 def tenant_list(args):
845     #print "tenant-list",args
846     if args.name:
847         toshow = _get_item_uuid("tenants", args.name)
848         URLrequest = "http://%s:%s/openmano/tenants/%s" %(mano_host, mano_port, toshow)
849     else:
850         URLrequest = "http://%s:%s/openmano/tenants" %(mano_host, mano_port)
851     mano_response = requests.get(URLrequest)
852     logger.debug("openmano response: %s", mano_response.text )
853     if args.verbose==None:
854         args.verbose=0
855     if args.name!=None:
856         args.verbose += 1
857     return _print_verbose(mano_response, args.verbose)
858
859 def tenant_delete(args):
860     #print "tenant-delete",args
861     todelete = _get_item_uuid("tenants", args.name)
862     if not args.force:
863         r = raw_input("Delete tenant %s (y/N)? " %(args.name))
864         if  not (len(r)>0  and r[0].lower()=="y"):
865             return 0
866     URLrequest = "http://%s:%s/openmano/tenants/%s" %(mano_host, mano_port, todelete)
867     mano_response = requests.delete(URLrequest)
868     logger.debug("openmano response: %s", mano_response.text )
869     result = 0 if mano_response.status_code==200 else mano_response.status_code
870     content = mano_response.json()
871     #print json.dumps(content, indent=4)
872     if mano_response.status_code == 200:
873         print content['result']
874     else:
875         print content['error']['description']
876     return result
877
878 def datacenter_attach(args):
879     tenant = _get_tenant()
880     datacenter = _get_datacenter(args.name)
881     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
882     
883     datacenter_dict={}
884     if args.vim_tenant_id != None:
885         datacenter_dict['vim_tenant'] = args.vim_tenant_id
886     if args.vim_tenant_name != None:
887         datacenter_dict['vim_tenant_name'] = args.vim_tenant_name
888     if args.user != None:
889         datacenter_dict['vim_username'] = args.user
890     if args.password != None:
891         datacenter_dict['vim_password'] = args.password
892     payload_req = json.dumps( {"datacenter": datacenter_dict })
893     
894     #print payload_req
895         
896     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, datacenter)
897     logger.debug("openmano request: %s", payload_req)
898     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
899     logger.debug("openmano response: %s", mano_response.text )
900     result = _print_verbose(mano_response, args.verbose)
901     #provide addional information if error
902     if mano_response.status_code != 200:
903         content = mano_response.json()
904         if "already in use for  'name'" in content['error']['description'] and \
905                 "to database vim_tenants table" in content['error']['description']:
906             print "Try to specify a different name with --vim-tenant-name"
907     return result
908
909 def datacenter_detach(args):
910     if args.all:
911         tenant = "any"
912     else:
913         tenant = _get_tenant()
914     datacenter = _get_datacenter(args.name, tenant)
915     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
916     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, datacenter)
917     mano_response = requests.delete(URLrequest, headers=headers_req)
918     logger.debug("openmano response: %s", mano_response.text )
919     content = mano_response.json()
920     #print json.dumps(content, indent=4)
921     result = 0 if mano_response.status_code==200 else mano_response.status_code
922     if mano_response.status_code == 200:
923         print content['result']
924     else:
925         print content['error']['description']
926     return result
927
928 def datacenter_create(args):
929     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
930     datacenter_dict={"name": args.name, "vim_url": args.url}
931     if args.description!=None:
932         datacenter_dict["description"] = args.description 
933     if args.type!=None:
934         datacenter_dict["type"] = args.type 
935     if args.url!=None:
936         datacenter_dict["vim_url_admin"] = args.url_admin 
937     if args.config!=None:
938         datacenter_dict["config"] = _load_file_or_yaml(args.config) 
939     payload_req = json.dumps( {"datacenter": datacenter_dict })
940     
941     #print payload_req
942         
943     URLrequest = "http://%s:%s/openmano/datacenters" %(mano_host, mano_port)
944     logger.debug("openmano request: %s", payload_req)
945     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
946     logger.debug("openmano response: %s", mano_response.text )
947     return _print_verbose(mano_response, args.verbose)
948
949 def datacenter_delete(args):
950     #print "datacenter-delete",args
951     todelete = _get_item_uuid("datacenters", args.name, "any")
952     if not args.force:
953         r = raw_input("Delete datacenter %s (y/N)? " %(args.name))
954         if  not (len(r)>0  and r[0].lower()=="y"):
955             return 0
956     URLrequest = "http://%s:%s/openmano/datacenters/%s" %(mano_host, mano_port, todelete)
957     mano_response = requests.delete(URLrequest)
958     logger.debug("openmano response: %s", mano_response.text )
959     result = 0 if mano_response.status_code==200 else mano_response.status_code
960     content = mano_response.json()
961     #print json.dumps(content, indent=4)
962     if mano_response.status_code == 200:
963         print content['result']
964     else:
965         print content['error']['description']
966     return result
967
968 def datacenter_list(args):
969     #print "datacenter-list",args
970     tenant='any' if args.all else _get_tenant()
971     
972     if args.name:
973         toshow = _get_item_uuid("datacenters", args.name, tenant) 
974         URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, toshow)
975     else:
976         URLrequest = "http://%s:%s/openmano/%s/datacenters" %(mano_host, mano_port, tenant)
977     mano_response = requests.get(URLrequest)
978     logger.debug("openmano response: %s", mano_response.text )
979     if args.verbose==None:
980         args.verbose=0
981     if args.name!=None:
982         args.verbose += 1
983     return _print_verbose(mano_response, args.verbose)
984
985 def vim_action(args):
986     #print "datacenter-net-action",args
987     tenant = _get_tenant()
988     datacenter = _get_datacenter(args.datacenter, tenant)
989     if args.verbose==None:
990         args.verbose=0
991     if args.action=="list":
992         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, tenant, datacenter, args.item)
993         if args.name!=None:
994             args.verbose += 1
995             URLrequest += "/" + args.name
996         mano_response = requests.get(URLrequest)
997         logger.debug("openmano response: %s", mano_response.text )
998         return _print_verbose(mano_response, args.verbose)
999     elif args.action=="delete":
1000         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss/%s" %(mano_host, mano_port, tenant, datacenter, args.item, args.name)
1001         mano_response = requests.delete(URLrequest)
1002         logger.debug("openmano response: %s", mano_response.text )
1003         result = 0 if mano_response.status_code==200 else mano_response.status_code
1004         content = mano_response.json()
1005         #print json.dumps(content, indent=4)
1006         if mano_response.status_code == 200:
1007             print content['result']
1008         else:
1009             print content['error']['description']
1010         return result
1011     elif args.action=="create":
1012         headers_req = {'content-type': 'application/yaml'}
1013         if args.file:
1014             create_dict = _load_file_or_yaml(args.file)
1015             if args.item not in create_dict:
1016                 create_dict = {args.item: create_dict}
1017         else:
1018             create_dict = {args.item:{}}
1019         if args.name:
1020             create_dict[args.item]['name'] = args.name
1021         #if args.description:
1022         #    create_dict[args.item]['description'] = args.description
1023         if args.item=="vim-net":
1024             if args.bind_net:
1025                 create_dict[args.item]['bind_net'] = args.bind_net
1026             if args.bind_type:
1027                 create_dict[args.item]['bind_type'] = args.bind_type
1028             if args.shared:
1029                 create_dict[args.item]['shared'] = args.shared
1030         if "name" not in create_dict[args.item]:
1031             print "You must provide a name in the descriptor file or with the --name option"
1032             return
1033         payload_req = yaml.safe_dump(create_dict, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
1034         logger.debug("openmano request: %s", payload_req)
1035         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, mano_tenant, datacenter, args.item)
1036         mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
1037         logger.debug("openmano response: %s", mano_response.text )
1038         if args.verbose==None:
1039             args.verbose=0
1040         return _print_verbose(mano_response, args.verbose)
1041
1042
1043 def datacenter_net_action(args):
1044     if args.action == "net-update":
1045         print "This command is deprecated, use 'openmano datacenter-netmap-delete --all' and 'openmano datacenter-netmap-import' instead!!!"
1046         print
1047         args.action = "netmap-delete"
1048         args.netmap = None
1049         args.all = True
1050         r = datacenter_netmap_action(args)
1051         if r == 0:
1052             args.force = True
1053             args.action = "netmap-import"
1054             r = datacenter_netmap_action(args)
1055         return r
1056
1057     if args.action == "net-edit":
1058         args.netmap = args.net
1059         args.name = None
1060     elif args.action == "net-list":
1061         args.netmap = None
1062     elif args.action == "net-delete":
1063         args.netmap = args.net
1064         args.all = False
1065           
1066     args.action = "netmap" + args.action[3:]
1067     args.vim_name=None
1068     args.vim_id=None
1069     print "This command is deprecated, use 'openmano datacenter-%s' instead!!!" % args.action
1070     print
1071     return datacenter_netmap_action(args)
1072
1073 def datacenter_netmap_action(args):
1074     tenant = _get_tenant()
1075     datacenter = _get_datacenter(args.datacenter, tenant)
1076     #print "datacenter_netmap_action",args
1077     payload_req = None
1078     if args.verbose==None:
1079         args.verbose=0
1080     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1081     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/netmaps" %(mano_host, mano_port, tenant, datacenter)
1082         
1083     if args.action=="netmap-list":
1084         if args.netmap:
1085             URLrequest += "/" + args.netmap
1086             args.verbose += 1
1087         mano_response = requests.get(URLrequest)
1088             
1089     elif args.action=="netmap-delete":
1090         if args.netmap and args.all:
1091             print "you can not use a netmap name and the option --all at the same time"
1092             return 1
1093         if args.netmap:
1094             force_text= "Delete default netmap '%s' from datacenter '%s' (y/N)? " % (args.netmap, datacenter)
1095             URLrequest += "/" + args.netmap
1096         elif args.all: 
1097             force_text="Delete all default netmaps from datacenter '%s' (y/N)? " % (datacenter)
1098         else:
1099             print "you must specify a netmap name or the option --all"
1100             return 1
1101         if not args.force:
1102             r = raw_input(force_text)
1103             if  len(r)>0  and r[0].lower()=="y":
1104                 pass
1105             else:
1106                 return 0
1107         mano_response = requests.delete(URLrequest, headers=headers_req)
1108     elif args.action=="netmap-import":
1109         if not args.force:
1110             r = raw_input("Create all the available networks from datacenter '%s' as default netmaps (y/N)? " % (datacenter))
1111             if  len(r)>0  and r[0].lower()=="y":
1112                 pass
1113             else:
1114                 return 0
1115         URLrequest += "/upload"
1116         mano_response = requests.post(URLrequest, headers=headers_req)
1117     elif args.action=="netmap-edit" or args.action=="netmap-create":
1118         if args.file:
1119             payload = _load_file_or_yaml(args.file)
1120         else:
1121             payload = {}
1122         if "netmap" not in payload:
1123             payload = {"netmap": payload}
1124         if args.name:
1125             payload["netmap"]["name"] = args.name
1126         if args.vim_id:
1127             payload["netmap"]["vim_id"] = args.vim_id
1128         if args.action=="netmap-create" and args.vim_name:
1129             payload["netmap"]["vim_name"] = args.vim_name
1130         payload_req = json.dumps(payload)
1131         logger.debug("openmano request: %s", payload_req)
1132         
1133         if args.action=="netmap-edit" and not args.force:
1134             if len(payload["netmap"]) == 0:
1135                 print "You must supply some parameter to edit"
1136                 return 1
1137             r = raw_input("Edit default netmap '%s' from datacenter '%s' (y/N)? " % (args.netmap, datacenter))
1138             if  len(r)>0  and r[0].lower()=="y":
1139                 pass
1140             else:
1141                 return 0
1142             URLrequest += "/" + args.netmap
1143             mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1144         else: #netmap-create
1145             if "vim_name" not in payload["netmap"] and "vim_id" not in payload["netmap"]:
1146                 print "You must supply either --vim-id or --vim-name option; or include one of them in the file descriptor"
1147                 return 1
1148             mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
1149
1150     logger.debug("openmano response: %s", mano_response.text )
1151     return _print_verbose(mano_response, args.verbose)
1152
1153 def element_edit(args):
1154     element = _get_item_uuid(args.element, args.name)
1155     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1156     URLrequest = "http://%s:%s/openmano/%s/%s" %(mano_host, mano_port, args.element, element)
1157     payload=_load_file_or_yaml(args.file)
1158     if args.element[:-1] not in payload:
1159         payload = {args.element[:-1]: payload }
1160     payload_req = json.dumps(payload)
1161     
1162     #print payload_req
1163     if not args.force or (args.name==None and args.filer==None):
1164         r = raw_input(" Edit " + args.element[:-1] + " " + args.name + " (y/N)? ")
1165         if  len(r)>0  and r[0].lower()=="y":
1166             pass
1167         else:
1168             return 0
1169     logger.debug("openmano request: %s", payload_req)
1170     mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1171     logger.debug("openmano response: %s", mano_response.text )
1172     if args.verbose==None:
1173         args.verbose=0
1174     if args.name!=None:
1175         args.verbose += 1
1176     return _print_verbose(mano_response, args.verbose)
1177
1178
1179 global mano_host
1180 global mano_port
1181 global mano_tenant
1182
1183 if __name__=="__main__":
1184     
1185     mano_tenant = os.getenv('OPENMANO_TENANT', None)
1186     mano_host = os.getenv('OPENMANO_HOST',"localhost")
1187     mano_port = os.getenv('OPENMANO_PORT',"9090")
1188     mano_datacenter = os.getenv('OPENMANO_DATACENTER',None)
1189     
1190     main_parser = ThrowingArgumentParser(description='User program to interact with OPENMANO-SERVER (openmanod)')
1191     main_parser.add_argument('--version', action='version', version='%(prog)s ' + __version__ )
1192     
1193     subparsers = main_parser.add_subparsers(help='commands')
1194     
1195     parent_parser = argparse.ArgumentParser(add_help=False)
1196     parent_parser.add_argument('--verbose', '-v', action='count', help="increase verbosity level. Use several times")
1197     parent_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
1198
1199     config_parser = subparsers.add_parser('config', parents=[parent_parser], help="prints configuration values")
1200     config_parser.add_argument("-n", action="store_true", help="resolves tenant and datacenter names")
1201     config_parser.set_defaults(func=config)
1202
1203     vnf_create_parser = subparsers.add_parser('vnf-create', parents=[parent_parser], help="adds a vnf into the catalogue")
1204     vnf_create_parser.add_argument("file", action="store", help="location of the JSON file describing the VNF").completer = FilesCompleter
1205     vnf_create_parser.add_argument("--name", action="store", help="name of the VNF (if it exists in the VNF descriptor, it is overwritten)")
1206     vnf_create_parser.add_argument("--description", action="store", help="description of the VNF (if it exists in the VNF descriptor, it is overwritten)")
1207     vnf_create_parser.add_argument("--image-path", action="store",  help="change image path locations (overwritten)")
1208     vnf_create_parser.set_defaults(func=vnf_create)
1209
1210     vnf_list_parser = subparsers.add_parser('vnf-list', parents=[parent_parser], help="lists information about a vnf")
1211     vnf_list_parser.add_argument("name", nargs='?', help="name of the VNF")
1212     vnf_list_parser.add_argument("-a", "--all", action="store_true", help="shows all vnfs, not only the owned or public ones")
1213     #vnf_list_parser.add_argument('--descriptor', help="prints the VNF descriptor", action="store_true")
1214     vnf_list_parser.set_defaults(func=vnf_list)
1215     
1216     vnf_delete_parser = subparsers.add_parser('vnf-delete', parents=[parent_parser], help="deletes a vnf from the catalogue")
1217     vnf_delete_parser.add_argument("name", action="store", help="name or uuid of the VNF to be deleted")
1218     vnf_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1219     vnf_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1220     vnf_delete_parser.set_defaults(func=vnf_delete)
1221     
1222     scenario_create_parser = subparsers.add_parser('scenario-create', parents=[parent_parser], help="adds a scenario into the OPENMANO DB")
1223     scenario_create_parser.add_argument("file", action="store", help="location of the YAML file describing the scenario").completer = FilesCompleter
1224     scenario_create_parser.add_argument("--name", action="store", help="name of the scenario (if it exists in the YAML scenario, it is overwritten)")
1225     scenario_create_parser.add_argument("--description", action="store", help="description of the scenario (if it exists in the YAML scenario, it is overwritten)")
1226     scenario_create_parser.set_defaults(func=scenario_create)
1227
1228     scenario_list_parser = subparsers.add_parser('scenario-list', parents=[parent_parser], help="lists information about a scenario")
1229     scenario_list_parser.add_argument("name", nargs='?', help="name of the scenario")
1230     #scenario_list_parser.add_argument('--descriptor', help="prints the scenario descriptor", action="store_true")
1231     scenario_list_parser.add_argument("-a", "--all", action="store_true", help="shows all scenarios, not only the owned or public ones")
1232     scenario_list_parser.set_defaults(func=scenario_list)
1233     
1234     scenario_delete_parser = subparsers.add_parser('scenario-delete', parents=[parent_parser], help="deletes a scenario from the OPENMANO DB")
1235     scenario_delete_parser.add_argument("name", action="store", help="name or uuid of the scenario to be deleted")
1236     scenario_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1237     scenario_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1238     scenario_delete_parser.set_defaults(func=scenario_delete)
1239
1240     scenario_deploy_parser = subparsers.add_parser('scenario-deploy', parents=[parent_parser], help="deploys a scenario")
1241     scenario_deploy_parser.add_argument("scenario", action="store", help="name or uuid of the scenario to be deployed")
1242     scenario_deploy_parser.add_argument("name", action="store", help="name of the instance")
1243     scenario_deploy_parser.add_argument("--nostart", action="store_true", help="does not start the vms, just reserve resources")
1244     scenario_deploy_parser.add_argument("--datacenter", action="store", help="specifies the datacenter. Needed if several datacenters are available")
1245     scenario_deploy_parser.add_argument("--description", action="store", help="description of the instance")
1246     scenario_deploy_parser.set_defaults(func=scenario_deploy)
1247     
1248     scenario_deploy_parser = subparsers.add_parser('scenario-verify', help="verifies if a scenario can be deployed (deploys it and deletes it)")
1249     scenario_deploy_parser.add_argument("scenario", action="store", help="name or uuid of the scenario to be verified")
1250     scenario_deploy_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
1251     scenario_deploy_parser.set_defaults(func=scenario_verify)
1252     
1253     instance_scenario_create_parser = subparsers.add_parser('instance-scenario-create', parents=[parent_parser], help="deploys a scenario")
1254     instance_scenario_create_parser.add_argument("file", nargs='?', help="descriptor of the instance. Must be a file or yaml/json text")
1255     instance_scenario_create_parser.add_argument("--scenario", action="store", help="name or uuid of the scenario to be deployed")
1256     instance_scenario_create_parser.add_argument("--name", action="store", help="name of the instance")
1257     instance_scenario_create_parser.add_argument("--nostart", action="store_true", help="does not start the vms, just reserve resources")
1258     instance_scenario_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter. Needed if several datacenters are available")
1259     instance_scenario_create_parser.add_argument("--netmap-use", action="append", type=str, dest="netmap_use", help="indicates a datacenter network to map a scenario network 'scenario-network=datacenter-network'. Can be used several times")
1260     instance_scenario_create_parser.add_argument("--netmap-create", action="append", type=str, dest="netmap_create", help="the scenario network must be created at datacenter 'scenario-network[=datacenter-network-name]' . Can be used several times")
1261     instance_scenario_create_parser.add_argument("--keypair", action="append", type=str, dest="keypair", help="public key for ssh access. Format '[user:]key1[,key2...]'. Can be used several times")
1262     instance_scenario_create_parser.add_argument("--keypair-auto", action="store_true", dest="keypair_auto", help="Inject the user ssh-keys found at $HOME/.ssh directory")
1263     instance_scenario_create_parser.add_argument("--description", action="store", help="description of the instance")
1264     instance_scenario_create_parser.set_defaults(func=instance_create)
1265
1266     instance_scenario_list_parser = subparsers.add_parser('instance-scenario-list', parents=[parent_parser], help="lists information about a scenario instance")
1267     instance_scenario_list_parser.add_argument("name", nargs='?', help="name of the scenario instance")
1268     instance_scenario_list_parser.add_argument("-a", "--all", action="store_true", help="shows all instance-scenarios, not only the owned")
1269     instance_scenario_list_parser.set_defaults(func=instance_scenario_list)
1270
1271     instance_scenario_delete_parser = subparsers.add_parser('instance-scenario-delete', parents=[parent_parser], help="deletes a scenario instance (and deletes all VM and net instances in VIM)")
1272     instance_scenario_delete_parser.add_argument("name", action="store", help="name or uuid of the scenario instance to be deleted")
1273     instance_scenario_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1274     instance_scenario_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1275     instance_scenario_delete_parser.set_defaults(func=instance_scenario_delete)
1276     
1277     instance_scenario_action_parser = subparsers.add_parser('instance-scenario-action', parents=[parent_parser], help="invoke an action over part or the whole scenario instance")
1278     instance_scenario_action_parser.add_argument("name", action="store", help="name or uuid of the scenario instance")
1279     instance_scenario_action_parser.add_argument("action", action="store", type=str, \
1280             choices=["start","pause","resume","shutoff","shutdown","forceOff","rebuild","reboot", "console"],\
1281             help="action to send")
1282     instance_scenario_action_parser.add_argument("param", nargs='?', help="addional param of the action. e.g. console type (novnc, ...), reboot type (TODO)")
1283     instance_scenario_action_parser.add_argument("--vnf", action="append", help="VNF to act on (can use several entries)")
1284     instance_scenario_action_parser.add_argument("--vm", action="append", help="VM to act on (can use several entries)")
1285     instance_scenario_action_parser.set_defaults(func=instance_scenario_action)
1286
1287     #instance_scenario_status_parser = subparsers.add_parser('instance-scenario-status', help="show the status of a scenario instance")
1288     #instance_scenario_status_parser.add_argument("name", action="store", help="name or uuid of the scenario instance")
1289     #instance_scenario_status_parser.set_defaults(func=instance_scenario_status)
1290     
1291     tenant_create_parser = subparsers.add_parser('tenant-create', parents=[parent_parser], help="creates a new tenant")
1292     tenant_create_parser.add_argument("name", action="store", help="name for the tenant")
1293     tenant_create_parser.add_argument("--description", action="store", help="description of the tenant")
1294     tenant_create_parser.set_defaults(func=tenant_create)
1295
1296     tenant_delete_parser = subparsers.add_parser('tenant-delete', parents=[parent_parser], help="deletes a tenant from the catalogue")
1297     tenant_delete_parser.add_argument("name", action="store", help="name or uuid of the tenant to be deleted")
1298     tenant_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1299     tenant_delete_parser.set_defaults(func=tenant_delete)
1300
1301     tenant_list_parser = subparsers.add_parser('tenant-list', parents=[parent_parser], help="lists information about a tenant")
1302     tenant_list_parser.add_argument("name", nargs='?', help="name or uuid of the tenant")
1303     tenant_list_parser.set_defaults(func=tenant_list)
1304
1305     item_list=('tenant','datacenter') #put tenant before so that help appear in order
1306     for item in item_list:
1307         element_edit_parser = subparsers.add_parser(item+'-edit', parents=[parent_parser], help="edits one "+item)
1308         element_edit_parser.add_argument("name", help="name or uuid of the "+item)
1309         element_edit_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter
1310         element_edit_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1311         element_edit_parser.set_defaults(func=element_edit, element=item + 's')
1312
1313     datacenter_create_parser = subparsers.add_parser('datacenter-create', parents=[parent_parser], help="creates a new datacenter")
1314     datacenter_create_parser.add_argument("name", action="store", help="name for the datacenter")
1315     datacenter_create_parser.add_argument("url", action="store", help="url for the datacenter")
1316     datacenter_create_parser.add_argument("--url_admin", action="store", help="url for administration for the datacenter")
1317     datacenter_create_parser.add_argument("--type", action="store", help="datacenter type: openstack or openvim (default)")
1318     datacenter_create_parser.add_argument("--config", action="store", help="aditional configuration in json/yaml format")
1319     datacenter_create_parser.add_argument("--description", action="store", help="description of the datacenter")
1320     datacenter_create_parser.set_defaults(func=datacenter_create)
1321
1322     datacenter_delete_parser = subparsers.add_parser('datacenter-delete', parents=[parent_parser], help="deletes a datacenter from the catalogue")
1323     datacenter_delete_parser.add_argument("name", action="store", help="name or uuid of the datacenter to be deleted")
1324     datacenter_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1325     datacenter_delete_parser.set_defaults(func=datacenter_delete)
1326
1327     datacenter_list_parser = subparsers.add_parser('datacenter-list', parents=[parent_parser], help="lists information about a datacenter")
1328     datacenter_list_parser.add_argument("name", nargs='?', help="name or uuid of the datacenter")
1329     datacenter_list_parser.add_argument("-a", "--all", action="store_true", help="shows all datacenters, not only datacenters attached to tenant")
1330     datacenter_list_parser.set_defaults(func=datacenter_list)
1331
1332     datacenter_attach_parser = subparsers.add_parser('datacenter-attach', parents=[parent_parser], help="associates a datacenter to the operating tenant")
1333     datacenter_attach_parser.add_argument("name", help="name or uuid of the datacenter")
1334     datacenter_attach_parser.add_argument('--vim-tenant-id', action='store', help="specify a datacenter tenant to use. A new one is created by default")
1335     datacenter_attach_parser.add_argument('--vim-tenant-name', action='store', help="specify a datacenter tenant name.")
1336     datacenter_attach_parser.add_argument("--user", action="store", help="user credentials for the datacenter")
1337     datacenter_attach_parser.add_argument("--password", action="store", help="password credentials for the datacenter")
1338     datacenter_attach_parser.set_defaults(func=datacenter_attach)
1339
1340     datacenter_detach_parser = subparsers.add_parser('datacenter-detach', parents=[parent_parser], help="removes the association between a datacenter and the operating tenant")
1341     datacenter_detach_parser.add_argument("name", help="name or uuid of the datacenter")
1342     datacenter_detach_parser.add_argument("-a", "--all", action="store_true", help="removes all associations from this datacenter")
1343     datacenter_detach_parser.set_defaults(func=datacenter_detach)
1344
1345
1346     action_dict={'net-update': 'retrieves external networks from datacenter',
1347                  'net-edit': 'edits an external network',
1348                  'net-delete': 'deletes an external network',
1349                  'net-list': 'lists external networks from a datacenter'
1350                  }
1351     for item in action_dict:
1352         datacenter_action_parser = subparsers.add_parser('datacenter-'+item, parents=[parent_parser], help=action_dict[item])
1353         datacenter_action_parser.add_argument("datacenter", help="name or uuid of the datacenter")
1354         if item=='net-edit' or item=='net-delete':
1355             datacenter_action_parser.add_argument("net", help="name or uuid of the datacenter net")
1356         if item=='net-edit':
1357             datacenter_action_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter
1358         if item!='net-list':
1359             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1360         datacenter_action_parser.set_defaults(func=datacenter_net_action, action=item)
1361
1362
1363     action_dict={'netmap-import': 'create network senario netmap base on the datacenter networks',
1364                  'netmap-create': 'create a new network senario netmap',
1365                  'netmap-edit':   'edit name of a network senario netmap',
1366                  'netmap-delete': 'deletes a network scenario netmap (--all for clearing all)',
1367                  'netmap-list':   'list/show network scenario netmaps'
1368                  }
1369     for item in action_dict:
1370         datacenter_action_parser = subparsers.add_parser('datacenter-'+item, parents=[parent_parser], help=action_dict[item])
1371         datacenter_action_parser.add_argument("--datacenter", help="name or uuid of the datacenter")
1372         #if item=='net-add':
1373         #    datacenter_action_parser.add_argument("net", help="name of the network")
1374         if item=='netmap-delete':
1375             datacenter_action_parser.add_argument("netmap", nargs='?',help="name or uuid of the datacenter netmap to delete")
1376             datacenter_action_parser.add_argument("--all", action="store_true", help="delete all netmap of this datacenter")
1377             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1378         if item=='netmap-edit':
1379             datacenter_action_parser.add_argument("netmap", help="name or uuid of the datacenter netmap do edit")
1380             datacenter_action_parser.add_argument("file", nargs='?', help="json/yaml text or file with the changes").completer = FilesCompleter
1381             datacenter_action_parser.add_argument("--name", action='store', help="name to assign to the datacenter netmap")
1382             datacenter_action_parser.add_argument('--vim-id', action='store', help="specify vim network uuid")
1383             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1384         if item=='netmap-list':
1385             datacenter_action_parser.add_argument("netmap", nargs='?',help="name or uuid of the datacenter netmap to show")
1386         if item=='netmap-create':
1387             datacenter_action_parser.add_argument("file", nargs='?', help="json/yaml text or file descriptor with the changes").completer = FilesCompleter
1388             datacenter_action_parser.add_argument("--name", action='store', help="name to assign to the datacenter netmap, by default same as vim-name")
1389             datacenter_action_parser.add_argument('--vim-id', action='store', help="specify vim network uuid")
1390             datacenter_action_parser.add_argument('--vim-name', action='store', help="specify vim network name")
1391         if item=='netmap-import':
1392             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1393         datacenter_action_parser.set_defaults(func=datacenter_netmap_action, action=item)
1394     
1395     for item in ("network", "tenant"):
1396         if item=="network":
1397             commnad_name = 'vim-net'
1398         else:
1399             commnad_name = 'vim-'+item
1400         vim_item_list_parser = subparsers.add_parser(commnad_name + '-list', parents=[parent_parser], help="list the vim " + item + "s")
1401         vim_item_list_parser.add_argument("name", nargs='?', help="name or uuid of the " + item + "s")
1402         vim_item_list_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1403         vim_item_list_parser.set_defaults(func=vim_action, item=item, action="list")
1404
1405         vim_item_del_parser = subparsers.add_parser(commnad_name + '-delete', parents=[parent_parser], help="list the vim " + item + "s")
1406         vim_item_del_parser.add_argument("name", help="name or uuid of the " + item + "s")
1407         vim_item_del_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1408         vim_item_del_parser.set_defaults(func=vim_action, item=item, action="delete")
1409
1410         vim_item_create_parser = subparsers.add_parser(commnad_name + '-create', parents=[parent_parser], help="create a "+item+" at vim")
1411         vim_item_create_parser.add_argument("file", nargs='?', help="descriptor of the %s. Must be a file or yaml/json text" % item).completer = FilesCompleter
1412         vim_item_create_parser.add_argument("--name", action="store", help="name of the %s" % item  )
1413         vim_item_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1414         if item=="network":
1415             vim_item_create_parser.add_argument("--type", action="store", help="type of network, data, ptp, bridge")
1416             vim_item_create_parser.add_argument("--shared", action="store_true", help="Private or shared")
1417             vim_item_create_parser.add_argument("--bind-net", action="store", help="For openvim datacenter type, net to be bind to, for vlan type, use sufix ':<vlan_tag>'")
1418         else:
1419             vim_item_create_parser.add_argument("--description", action="store", help="description of the %s" % item)
1420         vim_item_create_parser.set_defaults(func=vim_action, item=item, action="create")
1421
1422     argcomplete.autocomplete(main_parser)
1423     
1424     try:
1425         args = main_parser.parse_args()
1426         #logging info
1427         level = logging.CRITICAL
1428         streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1429         if "debug" in args and args.debug:
1430             level = logging.DEBUG
1431         logging.basicConfig(format=streamformat, level= level)
1432         logger = logging.getLogger('mano')
1433         logger.setLevel(level)
1434         result = args.func(args)
1435         if result == None:
1436             result = 0
1437         #for some reason it fails if call exit inside try instance. Need to call exit at the end !?
1438     except (requests.exceptions.ConnectionError):
1439         print "Connection error: not possible to contact OPENMANO-SERVER (openmanod)"
1440         result = -2
1441     except (KeyboardInterrupt):
1442         print 'Exiting openmano'
1443         result = -3
1444     except (SystemExit, ArgumentParserError):
1445         result = -4
1446     except OpenmanoCLIError as e:
1447         print str(e)
1448         result = -5
1449     
1450     #print result
1451     exit(result)
1452