Merge branch 'v1.0'
[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.8-r512"
32 version_date="Oct 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 or args.image_name or args.image_checksum:
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             if args.image_name:
268                 index=0
269                 for image_name_ in args.image_name.split(","):
270                     myvnf['vnf']['VNFC'][index]['image name']=image_name_
271                     index=index+1
272             if args.image_checksum:
273                 index=0
274                 for image_checksum_ in args.image_checksum.split(","):
275                     myvnf['vnf']['VNFC'][index]['image checksum']=image_checksum_
276                     index=index+1
277         except (KeyError, TypeError), e:
278             if str(e)=='vnf':           error_pos= "missing field 'vnf'"
279             elif str(e)=='name':        error_pos= "missing field  'vnf':'name'"
280             elif str(e)=='description': error_pos= "missing field  'vnf':'description'"
281             elif str(e)=='VNFC':        error_pos= "missing field  'vnf':'VNFC'"
282             elif str(e)==str(index):    error_pos= "field  'vnf':'VNFC' must be an array"
283             elif str(e)=='VNFC image':  error_pos= "missing field 'vnf':'VNFC'['VNFC image']"
284             elif str(e)=='image name':  error_pos= "missing field 'vnf':'VNFC'['image name']"
285             elif str(e)=='image checksum':  error_pos= "missing field 'vnf':'VNFC'['image checksum']"
286             else:                       error_pos="wrong format"
287             print "Wrong VNF descriptor: " + error_pos
288             return -1 
289     payload_req = json.dumps(myvnf)
290         
291     #print payload_req
292         
293     URLrequest = "http://%s:%s/openmano/%s/vnfs" %(mano_host, mano_port, tenant)
294     logger.debug("openmano request: %s", payload_req)
295     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
296     logger.debug("openmano response: %s", mano_response.text )
297
298     return _print_verbose(mano_response, args.verbose)
299
300 def vnf_list(args):
301     #print "vnf-list",args
302     if args.all:
303         tenant = "any"
304     else:
305         tenant = _get_tenant()
306     if args.name:
307         toshow = _get_item_uuid("vnfs", args.name, tenant)
308         URLrequest = "http://%s:%s/openmano/%s/vnfs/%s" %(mano_host, mano_port, tenant, toshow)
309     else:
310         URLrequest = "http://%s:%s/openmano/%s/vnfs" %(mano_host, mano_port, tenant)
311     mano_response = requests.get(URLrequest)
312     logger.debug("openmano response: %s", mano_response.text )
313     content = mano_response.json()
314     #print json.dumps(content, indent=4)
315     if args.verbose==None:
316         args.verbose=0
317     result = 0 if mano_response.status_code==200 else mano_response.status_code
318     if mano_response.status_code == 200:
319         if not args.name:
320             if args.verbose >= 3:
321                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
322                 return result
323             if len(content['vnfs']) == 0:
324                 print "No VNFs were found."
325                 return 404 #HTTP_Not_Found
326             for vnf in content['vnfs']:
327                 myoutput = "%s %s" %(vnf['uuid'].ljust(38),vnf['name'].ljust(20))
328                 if args.verbose >=1:
329                     myoutput = "%s %s" %(myoutput, vnf['created_at'].ljust(20))
330                 print myoutput
331                 if args.verbose >=2:
332                     print "  Description: %s" %vnf['description']
333                     print "  VNF descriptor file: %s" %vnf['path']
334         else:
335             if args.verbose:
336                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
337                 return result
338             vnf = content['vnf']
339             print "%s %s %s" %(vnf['uuid'].ljust(38),vnf['name'].ljust(20), vnf['created_at'].ljust(20))
340             print "  Description: %s" %vnf['description']
341             #print "  VNF descriptor file: %s" %vnf['path']
342             print "    VMs:"
343             for vm in vnf['VNFC']:
344                 #print "    %s %s %s" %(vm['name'].ljust(20), vm['uuid'].ljust(38), vm['description'].ljust(30))
345                 print "        %s %s" %(vm['name'].ljust(20), vm['description'])
346             if len(vnf['nets'])>0:
347                 print "    Internal nets:"
348                 for net in vnf['nets']:
349                     print "        %s %s" %(net['name'].ljust(20), net['description'])
350             if len(vnf['external-connections'])>0:
351                 print "    External interfaces:"
352                 for interface in vnf['external-connections']:
353                     print "        %s %s %s %s" %(interface['external_name'].ljust(20), interface['vm_name'].ljust(20), interface['internal_name'].ljust(20), \
354                                                   interface['vpci'].ljust(14))
355     else:
356         print content['error']['description']
357         if args.verbose:
358             print yaml.safe_dump(content, indent=4, default_flow_style=False)
359     return result
360
361 def vnf_delete(args):
362     #print "vnf-delete",args
363     if args.all:
364         tenant = "any"
365     else:
366         tenant = _get_tenant()
367     todelete = _get_item_uuid("vnfs", args.name, tenant=tenant)
368     if not args.force:
369         r = raw_input("Delete VNF %s (y/N)? " %(todelete))
370         if  not (len(r)>0  and r[0].lower()=="y"):
371             return 0
372     URLrequest = "http://%s:%s/openmano/%s/vnfs/%s" %(mano_host, mano_port, tenant, todelete)
373     mano_response = requests.delete(URLrequest)
374     logger.debug("openmano response: %s", mano_response.text )
375     result = 0 if mano_response.status_code==200 else mano_response.status_code
376     content = mano_response.json()
377     #print json.dumps(content, indent=4)
378     if mano_response.status_code == 200:
379         print content['result']
380     else:
381         print content['error']['description']
382     return result
383
384 def scenario_create(args):
385     #print "scenario-create",args
386     tenant = _get_tenant()
387     headers_req = {'content-type': 'application/yaml'}
388     myscenario = _load_file_or_yaml(args.file)
389
390     if args.name:
391         myscenario['name'] = args.name
392     if args.description:
393         myscenario['description'] = args.description
394     payload_req = yaml.safe_dump(myscenario, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
395     
396     #print payload_req
397         
398     URLrequest = "http://%s:%s/openmano/%s/scenarios" %(mano_host, mano_port, tenant)
399     logger.debug("openmano request: %s", payload_req)
400     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
401     logger.debug("openmano response: %s", mano_response.text )
402     return _print_verbose(mano_response, args.verbose)
403
404 def scenario_list(args):
405     #print "scenario-list",args
406     if args.all:
407         tenant = "any"
408     else:
409         tenant = _get_tenant()
410     if args.name:
411         toshow = _get_item_uuid("scenarios", args.name, tenant)
412         URLrequest = "http://%s:%s/openmano/%s/scenarios/%s" %(mano_host, mano_port, tenant, toshow)
413     else:
414         URLrequest = "http://%s:%s/openmano/%s/scenarios" %(mano_host, mano_port, tenant)
415     mano_response = requests.get(URLrequest)
416     logger.debug("openmano response: %s", mano_response.text )
417     content = mano_response.json()
418     #print json.dumps(content, indent=4)
419     if args.verbose==None:
420         args.verbose=0
421
422     result = 0 if mano_response.status_code==200 else mano_response.status_code
423     if mano_response.status_code == 200:
424         if not args.name:
425             if args.verbose >= 3:
426                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
427                 return result
428             if len(content['scenarios']) == 0:
429                 print "No scenarios were found."
430                 return 404 #HTTP_Not_Found
431             for scenario in content['scenarios']:
432                 myoutput = "%s %s" %(scenario['uuid'].ljust(38),scenario['name'].ljust(20))
433                 if args.verbose >=1:
434                     myoutput = "%s %s" %(myoutput, scenario['created_at'].ljust(20))
435                 print myoutput
436                 if args.verbose >=2:
437                     print "  Description: %s" %scenario['description']
438         else:
439             if args.verbose:
440                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
441                 return result
442             scenario = content['scenario']
443             myoutput = "%s %s %s" %(scenario['uuid'].ljust(38),scenario['name'].ljust(20), scenario['created_at'].ljust(20))
444             print myoutput
445             print "  Description: %s" %scenario['description']
446             print "    VNFs:"
447             for vnf in scenario['vnfs']:
448                 print "        %s %s %s" %(vnf['name'].ljust(20), vnf['vnf_id'].ljust(38), vnf['description'])
449             if len(scenario['nets'])>0:
450                 print "    Internal nets:"
451                 for net in scenario['nets']:
452                     if net['description'] is None:   #if description does not exist, description is "-". Valid for external and internal nets.
453                         net['description'] = '-' 
454                     if not net['external']:
455                         print "        %s %s %s" %(net['name'].ljust(20), net['uuid'].ljust(38), net['description'].ljust(30))
456                 print "    External nets:"
457                 for net in scenario['nets']:
458                     if net['external']:
459                         print "        %s %s %s vim-id:%s" %(net['name'].ljust(20), net['uuid'].ljust(38), net['description'].ljust(30), net['vim_id'])
460     else:
461         print content['error']['description']
462         if args.verbose:
463             print yaml.safe_dump(content, indent=4, default_flow_style=False)
464     return result
465
466 def scenario_delete(args):
467     #print "scenario-delete",args
468     if args.all:
469         tenant = "any"
470     else:
471         tenant = _get_tenant()
472     todelete = _get_item_uuid("scenarios", args.name, tenant=tenant)
473     if not args.force:
474         r = raw_input("Delete scenario %s (y/N)? " %(args.name))
475         if  not (len(r)>0  and r[0].lower()=="y"):
476             return 0
477     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s" %(mano_host, mano_port, tenant, todelete)
478     mano_response = requests.delete(URLrequest)
479     logger.debug("openmano response: %s", mano_response.text )
480     result = 0 if mano_response.status_code==200 else mano_response.status_code
481     content = mano_response.json()
482     #print json.dumps(content, indent=4)
483     if mano_response.status_code == 200:
484         print content['result']
485     else:
486         print content['error']['description']
487     return result
488
489 def scenario_deploy(args):
490     print "This command is deprecated, use 'openmano instance-scenario-create --scenario %s --name %s' instead!!!" % (args.scenario, args.name)
491     print
492     args.file = None
493     args.netmap_use = None
494     args.netmap_create = None
495     args.keypair = None
496     args.keypair_auto = None
497     return instance_create(args)
498
499 #     #print "scenario-deploy",args
500 #     headers_req = {'content-type': 'application/json'}
501 #     action = {}
502 #     actionCmd="start"
503 #     if args.nostart:
504 #         actionCmd="reserve"
505 #     action[actionCmd] = {}
506 #     action[actionCmd]["instance_name"] = args.name
507 #     if args.datacenter != None:
508 #         action[actionCmd]["datacenter"] = args.datacenter
509 #     elif mano_datacenter != None:
510 #         action[actionCmd]["datacenter"] = mano_datacenter
511 #         
512 #     if args.description:
513 #         action[actionCmd]["description"] = args.description
514 #     payload_req = json.dumps(action, indent=4)
515 #     #print payload_req
516
517 #     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, mano_tenant, args.scenario)
518 #     logger.debug("openmano request: %s", payload_req)
519 #     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
520 #     logger.debug("openmano response: %s", mano_response.text )
521 #     if args.verbose==None:
522 #         args.verbose=0
523 #     
524 #     result = 0 if mano_response.status_code==200 else mano_response.status_code
525 #     content = mano_response.json()
526 #     #print json.dumps(content, indent=4)
527 #     if args.verbose >= 3:
528 #         print yaml.safe_dump(content, indent=4, default_flow_style=False)
529 #         return result
530
531 #     if mano_response.status_code == 200:
532 #         myoutput = "%s %s" %(content['uuid'].ljust(38),content['name'].ljust(20))
533 #         if args.verbose >=1:
534 #             myoutput = "%s %s" %(myoutput, content['created_at'].ljust(20))
535 #         if args.verbose >=2:
536 #             myoutput = "%s %s %s" %(myoutput, content['description'].ljust(30))
537 #         print myoutput
538 #         print ""
539 #         print "To check the status, run the following command:"
540 #         print "openmano instance-scenario-list <instance_id>"
541 #     else:
542 #         print content['error']['description']
543 #     return result
544
545 def scenario_verify(args):
546     #print "scenario-verify",args
547     headers_req = {'content-type': 'application/json'}
548     action = {}
549     action["verify"] = {}
550     action["verify"]["instance_name"] = "scen-verify-return5"
551     payload_req = json.dumps(action, indent=4)
552     #print payload_req
553
554     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, mano_tenant, args.scenario)
555     logger.debug("openmano request: %s", payload_req)
556     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
557     logger.debug("openmano response: %s", mano_response.text )
558     
559     result = 0 if mano_response.status_code==200 else mano_response.status_code
560     content = mano_response.json()
561     #print json.dumps(content, indent=4)
562     if mano_response.status_code == 200:
563         print content['result']
564     else:
565         print content['error']['description']
566     return result
567
568 def instance_create(args):
569     tenant = _get_tenant()
570     headers_req = {'content-type': 'application/yaml'}
571     myInstance={"instance": {}, "schema_version": "0.1"}
572     if args.file:
573         instance_dict = _load_file_or_yaml(args.file)
574         if "instance" not in instance_dict:
575             myInstance = {"instance": instance_dict, "schema_version": "0.1"}
576         else:
577             myInstance = instance_dict
578     if args.name:
579         myInstance["instance"]['name'] = args.name
580     if args.description:
581         myInstance["instance"]['description'] = args.description
582     if args.nostart:
583         myInstance["instance"]['action'] = "reserve"
584     #datacenter
585     datacenter = myInstance["instance"].get("datacenter")
586     if args.datacenter != None:
587         datacenter = args.datacenter
588     myInstance["instance"]["datacenter"] = _get_datacenter(datacenter, tenant)
589     #scenario
590     scenario = myInstance["instance"].get("scenario")
591     if args.scenario != None:
592         scenario = args.scenario
593     if not scenario:
594         print "you must provide a scenario in the file descriptor or with --scenario"
595         return -1
596     myInstance["instance"]["scenario"] = _get_item_uuid("scenarios", scenario, tenant)
597     if args.netmap_use:
598         if "networks" not in myInstance["instance"]:
599             myInstance["instance"]["networks"] = {}
600         for net in args.netmap_use:
601             net_comma_list = net.split(",")
602             for net_comma in net_comma_list:
603                 net_tuple = net_comma.split("=")
604                 if len(net_tuple) != 2:
605                     print "error at netmap-use. Expected net-scenario=net-datacenter. (%s)?" % net_comma
606                     return
607                 net_scenario   = net_tuple[0].strip()
608                 net_datacenter = net_tuple[1].strip()
609                 if net_scenario not in myInstance["instance"]["networks"]:
610                     myInstance["instance"]["networks"][net_scenario] = {} 
611                 if "sites" not in myInstance["instance"]["networks"][net_scenario]:
612                     myInstance["instance"]["networks"][net_scenario]["sites"] = [ {} ]
613                 myInstance["instance"]["networks"][net_scenario]["sites"][0]["netmap-use"] = net_datacenter
614     if args.netmap_create:
615         if "networks" not in myInstance["instance"]:
616             myInstance["instance"]["networks"] = {}
617         for net in args.netmap_create:
618             net_comma_list = net.split(",")
619             for net_comma in net_comma_list:
620                 net_tuple = net_comma.split("=")
621                 if len(net_tuple) == 1:
622                     net_scenario   = net_tuple[0].strip()
623                     net_datacenter = None
624                 elif len(net_tuple) == 2:
625                     net_scenario   = net_tuple[0].strip()
626                     net_datacenter = net_tuple[1].strip()
627                 else:
628                     print "error at netmap-create. Expected net-scenario=net-datacenter or net-scenario. (%s)?" % net_comma
629                     return
630                 if net_scenario not in myInstance["instance"]["networks"]:
631                     myInstance["instance"]["networks"][net_scenario] = {} 
632                 if "sites" not in myInstance["instance"]["networks"][net_scenario]:
633                     myInstance["instance"]["networks"][net_scenario]["sites"] = [ {} ]
634                 myInstance["instance"]["networks"][net_scenario]["sites"][0]["netmap-create"] = net_datacenter
635     if args.keypair:
636         if "cloud-config" not in myInstance["instance"]:
637             myInstance["instance"]["cloud-config"] = {}
638         cloud_config = myInstance["instance"]["cloud-config"]
639         for key in args.keypair:
640             index = key.find(":")
641             if index<0:
642                 if "key-pairs" not in cloud_config:
643                     cloud_config["key-pairs"] = []
644                 cloud_config["key-pairs"].append(key)
645             else:
646                 user = key[:index]
647                 key_ = key[index+1:]
648                 key_list = key_.split(",")
649                 if "users" not in cloud_config:
650                     cloud_config["users"] = []
651                 cloud_config["users"].append({"name": user, "key-pairs": key_list  })
652     if args.keypair_auto:
653         try:
654             keys=[]
655             home = os.getenv("HOME")
656             user = os.getenv("USER")
657             files = os.listdir(home+'/.ssh')
658             for file in files:
659                 if file[-4:] == ".pub":
660                     with open(home+'/.ssh/'+file, 'r') as f:
661                         keys.append(f.read())
662             if not keys:
663                 print "Cannot obtain any public ssh key from '{}'. Try not using --keymap-auto".format(home+'/.ssh')
664                 return 1
665         except Exception as e:
666             print "Cannot obtain any public ssh key. Error '{}'. Try not using --keymap-auto".format(str(e))
667             return 1
668         
669         if "cloud-config" not in myInstance["instance"]:
670             myInstance["instance"]["cloud-config"] = {}
671         cloud_config = myInstance["instance"]["cloud-config"]
672         if "key-pairs" not in cloud_config:
673             cloud_config["key-pairs"] = []
674         if user:
675             if "users" not in cloud_config:
676                 cloud_config["users"] = []
677             cloud_config["users"].append({"name": user, "key-pairs": keys })                    
678                         
679     payload_req = yaml.safe_dump(myInstance, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
680     logger.debug("openmano request: %s", payload_req)
681     URLrequest = "http://%s:%s/openmano/%s/instances" %(mano_host, mano_port, tenant)
682     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
683     logger.debug("openmano response: %s", mano_response.text )
684     if args.verbose==None:
685         args.verbose=0
686     
687     result = 0 if mano_response.status_code==200 else mano_response.status_code
688     content = mano_response.json()
689     #print json.dumps(content, indent=4)
690     if args.verbose >= 3:
691         print yaml.safe_dump(content, indent=4, default_flow_style=False)
692         return result
693
694     if mano_response.status_code == 200:
695         myoutput = "%s %s" %(content['uuid'].ljust(38),content['name'].ljust(20))
696         if args.verbose >=1:
697             myoutput = "%s %s" %(myoutput, content['created_at'].ljust(20))
698         if args.verbose >=2:
699             myoutput = "%s %s %s" %(myoutput, content['description'].ljust(30))
700         print myoutput
701     else:
702         print content['error']['description']
703     return result
704
705 def instance_scenario_list(args):
706     #print "instance-scenario-list",args
707     if args.all:
708         tenant = "any"
709     else:
710         tenant = _get_tenant()
711     if args.name:
712         toshow = _get_item_uuid("instances", args.name, tenant)
713         URLrequest = "http://%s:%s/openmano/%s/instances/%s" %(mano_host, mano_port, tenant, toshow)
714     else:
715         URLrequest = "http://%s:%s/openmano/%s/instances" %(mano_host, mano_port, tenant)
716     mano_response = requests.get(URLrequest)
717     logger.debug("openmano response: %s", mano_response.text )
718     content = mano_response.json()
719     #print json.dumps(content, indent=4)
720     if args.verbose==None:
721         args.verbose=0
722
723     result = 0 if mano_response.status_code==200 else mano_response.status_code
724     if mano_response.status_code == 200:
725         if not args.name:
726             if args.verbose >= 3:
727                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
728                 return result
729             if len(content['instances']) == 0:
730                 print "No scenario instances were found."
731                 return result
732             for instance in content['instances']:
733                 myoutput = "%s %s" %(instance['uuid'].ljust(38),instance['name'].ljust(20))
734                 if args.verbose >=1:
735                     myoutput = "%s %s" %(myoutput, instance['created_at'].ljust(20))
736                 print myoutput
737                 if args.verbose >=2:
738                     print "Description: %s" %instance['description']
739         else:
740             if args.verbose:
741                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
742                 return result
743             instance = content
744             print "%s %s %s" %(instance['uuid'].ljust(38),instance['name'].ljust(20),instance['created_at'].ljust(20))
745             print "Description: %s" %instance['description']
746             print "Template scenario id: %s" %instance['scenario_id']
747             print "Template scenario name: %s" %instance['scenario_name']
748             print "---------------------------------------"
749             print "VNF instances: %d" %len(instance['vnfs'])
750             for vnf in instance['vnfs']:
751                 #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))
752                 print "    %s %s Template vnf id: %s" %(vnf['uuid'].ljust(38), vnf['vnf_name'].ljust(20), vnf['vnf_id'].ljust(38))
753             if len(instance['nets'])>0:
754                 print "---------------------------------------"
755                 print "Internal nets:"
756                 for net in instance['nets']:
757                     if net['created']:
758                         print "    %s %s VIM ID: %s" %(net['uuid'].ljust(38), net['status'].ljust(12), net['vim_net_id'])
759                 print "---------------------------------------"
760                 print "External nets:"
761                 for net in instance['nets']:
762                     if not net['created']:
763                         print "    %s %s VIM ID: %s" %(net['uuid'].ljust(38), net['status'].ljust(12), net['vim_net_id'])
764             print "---------------------------------------"
765             print "VM instances:"
766             for vnf in instance['vnfs']:
767                 for vm in vnf['vms']:
768                     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'])
769     else:
770         print content['error']['description']
771         if args.verbose:
772             print yaml.safe_dump(content, indent=4, default_flow_style=False)
773     return result
774
775 def instance_scenario_status(args):
776     print "instance-scenario-status"
777     return 0
778
779 def instance_scenario_delete(args):
780     if args.all:
781         tenant = "any"
782     else:
783         tenant = _get_tenant()
784     todelete = _get_item_uuid("instances", args.name, tenant=tenant)
785     #print "instance-scenario-delete",args
786     if not args.force:
787         r = raw_input("Delete scenario instance %s (y/N)? " %(args.name))
788         if  not (len(r)>0  and r[0].lower()=="y"):
789             return
790     URLrequest = "http://%s:%s/openmano/%s/instances/%s" %(mano_host, mano_port, tenant, todelete)
791     mano_response = requests.delete(URLrequest)
792     logger.debug("openmano response: %s", mano_response.text )
793     result = 0 if mano_response.status_code==200 else mano_response.status_code
794     content = mano_response.json()
795     #print json.dumps(content, indent=4)
796     if mano_response.status_code == 200:
797         print content['result']
798     else:
799         print content['error']['description']
800     return result
801
802 def instance_scenario_action(args):
803     #print "instance-scenario-action", args
804     tenant = _get_tenant()
805     toact = _get_item_uuid("instances", args.name, tenant=tenant)
806     action={}
807     action[ args.action ] = args.param
808     if args.vnf:
809         action["vnfs"] = args.vnf
810     if args.vm:
811         action["vms"] = args.vm
812     
813     headers_req = {'content-type': 'application/json'}
814     payload_req = json.dumps(action, indent=4)
815     URLrequest = "http://%s:%s/openmano/%s/instances/%s/action" %(mano_host, mano_port, tenant, toact)
816     logger.debug("openmano request: %s", payload_req)
817     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
818     logger.debug("openmano response: %s", mano_response.text )
819     result = 0 if mano_response.status_code==200 else mano_response.status_code
820     content = mano_response.json()
821     #print json.dumps(content, indent=4)
822     if mano_response.status_code == 200:
823         if args.verbose:
824             print yaml.safe_dump(content, indent=4, default_flow_style=False)
825             return result
826         for uuid,c in content.iteritems():
827             print "%s %s %s" %(uuid.ljust(38), c['name'].ljust(20),c['description'].ljust(20))
828     else:
829         print content['error']['description']
830     return result
831
832
833 def instance_vnf_list(args):
834     print "instance-vnf-list"
835     return 0
836
837 def instance_vnf_status(args):
838     print "instance-vnf-status"
839     return 0
840
841 def tenant_create(args):
842     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
843     tenant_dict={"name": args.name}
844     if args.description!=None:
845         tenant_dict["description"] = args.description 
846     payload_req = json.dumps( {"tenant": tenant_dict })
847     
848     #print payload_req
849         
850     URLrequest = "http://%s:%s/openmano/tenants" %(mano_host, mano_port)
851     logger.debug("openmano request: %s", payload_req)
852     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
853     logger.debug("openmano response: %s", mano_response.text )
854     return _print_verbose(mano_response, args.verbose)
855
856 def tenant_list(args):
857     #print "tenant-list",args
858     if args.name:
859         toshow = _get_item_uuid("tenants", args.name)
860         URLrequest = "http://%s:%s/openmano/tenants/%s" %(mano_host, mano_port, toshow)
861     else:
862         URLrequest = "http://%s:%s/openmano/tenants" %(mano_host, mano_port)
863     mano_response = requests.get(URLrequest)
864     logger.debug("openmano response: %s", mano_response.text )
865     if args.verbose==None:
866         args.verbose=0
867     if args.name!=None:
868         args.verbose += 1
869     return _print_verbose(mano_response, args.verbose)
870
871 def tenant_delete(args):
872     #print "tenant-delete",args
873     todelete = _get_item_uuid("tenants", args.name)
874     if not args.force:
875         r = raw_input("Delete tenant %s (y/N)? " %(args.name))
876         if  not (len(r)>0  and r[0].lower()=="y"):
877             return 0
878     URLrequest = "http://%s:%s/openmano/tenants/%s" %(mano_host, mano_port, todelete)
879     mano_response = requests.delete(URLrequest)
880     logger.debug("openmano response: %s", mano_response.text )
881     result = 0 if mano_response.status_code==200 else mano_response.status_code
882     content = mano_response.json()
883     #print json.dumps(content, indent=4)
884     if mano_response.status_code == 200:
885         print content['result']
886     else:
887         print content['error']['description']
888     return result
889
890 def datacenter_attach(args):
891     tenant = _get_tenant()
892     datacenter = _get_datacenter(args.name)
893     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
894     
895     datacenter_dict={}
896     if args.vim_tenant_id != None:
897         datacenter_dict['vim_tenant'] = args.vim_tenant_id
898     if args.vim_tenant_name != None:
899         datacenter_dict['vim_tenant_name'] = args.vim_tenant_name
900     if args.user != None:
901         datacenter_dict['vim_username'] = args.user
902     if args.password != None:
903         datacenter_dict['vim_password'] = args.password
904     if args.config!=None:
905         datacenter_dict["config"] = _load_file_or_yaml(args.config)
906     payload_req = json.dumps( {"datacenter": datacenter_dict })
907     
908     #print payload_req
909         
910     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, datacenter)
911     logger.debug("openmano request: %s", payload_req)
912     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
913     logger.debug("openmano response: %s", mano_response.text )
914     result = _print_verbose(mano_response, args.verbose)
915     #provide addional information if error
916     if mano_response.status_code != 200:
917         content = mano_response.json()
918         if "already in use for  'name'" in content['error']['description'] and \
919                 "to database vim_tenants table" in content['error']['description']:
920             print "Try to specify a different name with --vim-tenant-name"
921     return result
922
923 def datacenter_detach(args):
924     if args.all:
925         tenant = "any"
926     else:
927         tenant = _get_tenant()
928     datacenter = _get_datacenter(args.name, tenant)
929     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
930     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, datacenter)
931     mano_response = requests.delete(URLrequest, headers=headers_req)
932     logger.debug("openmano response: %s", mano_response.text )
933     content = mano_response.json()
934     #print json.dumps(content, indent=4)
935     result = 0 if mano_response.status_code==200 else mano_response.status_code
936     if mano_response.status_code == 200:
937         print content['result']
938     else:
939         print content['error']['description']
940     return result
941
942 def datacenter_create(args):
943     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
944     datacenter_dict={"name": args.name, "vim_url": args.url}
945     if args.description!=None:
946         datacenter_dict["description"] = args.description 
947     if args.type!=None:
948         datacenter_dict["type"] = args.type 
949     if args.url!=None:
950         datacenter_dict["vim_url_admin"] = args.url_admin 
951     if args.config!=None:
952         datacenter_dict["config"] = _load_file_or_yaml(args.config) 
953     payload_req = json.dumps( {"datacenter": datacenter_dict })
954     
955     #print payload_req
956         
957     URLrequest = "http://%s:%s/openmano/datacenters" %(mano_host, mano_port)
958     logger.debug("openmano request: %s", payload_req)
959     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
960     logger.debug("openmano response: %s", mano_response.text )
961     return _print_verbose(mano_response, args.verbose)
962
963 def datacenter_delete(args):
964     #print "datacenter-delete",args
965     todelete = _get_item_uuid("datacenters", args.name, "any")
966     if not args.force:
967         r = raw_input("Delete datacenter %s (y/N)? " %(args.name))
968         if  not (len(r)>0  and r[0].lower()=="y"):
969             return 0
970     URLrequest = "http://%s:%s/openmano/datacenters/%s" %(mano_host, mano_port, todelete)
971     mano_response = requests.delete(URLrequest)
972     logger.debug("openmano response: %s", mano_response.text )
973     result = 0 if mano_response.status_code==200 else mano_response.status_code
974     content = mano_response.json()
975     #print json.dumps(content, indent=4)
976     if mano_response.status_code == 200:
977         print content['result']
978     else:
979         print content['error']['description']
980     return result
981
982 def datacenter_list(args):
983     #print "datacenter-list",args
984     tenant='any' if args.all else _get_tenant()
985     
986     if args.name:
987         toshow = _get_item_uuid("datacenters", args.name, tenant) 
988         URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, toshow)
989     else:
990         URLrequest = "http://%s:%s/openmano/%s/datacenters" %(mano_host, mano_port, tenant)
991     mano_response = requests.get(URLrequest)
992     logger.debug("openmano response: %s", mano_response.text )
993     if args.verbose==None:
994         args.verbose=0
995     if args.name!=None:
996         args.verbose += 1
997     return _print_verbose(mano_response, args.verbose)
998
999 def vim_action(args):
1000     #print "datacenter-net-action",args
1001     tenant = _get_tenant()
1002     datacenter = _get_datacenter(args.datacenter, tenant)
1003     if args.verbose==None:
1004         args.verbose=0
1005     if args.action=="list":
1006         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, tenant, datacenter, args.item)
1007         if args.name!=None:
1008             args.verbose += 1
1009             URLrequest += "/" + args.name
1010         mano_response = requests.get(URLrequest)
1011         logger.debug("openmano response: %s", mano_response.text )
1012         return _print_verbose(mano_response, args.verbose)
1013     elif args.action=="delete":
1014         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss/%s" %(mano_host, mano_port, tenant, datacenter, args.item, args.name)
1015         mano_response = requests.delete(URLrequest)
1016         logger.debug("openmano response: %s", mano_response.text )
1017         result = 0 if mano_response.status_code==200 else mano_response.status_code
1018         content = mano_response.json()
1019         #print json.dumps(content, indent=4)
1020         if mano_response.status_code == 200:
1021             print content['result']
1022         else:
1023             print content['error']['description']
1024         return result
1025     elif args.action=="create":
1026         headers_req = {'content-type': 'application/yaml'}
1027         if args.file:
1028             create_dict = _load_file_or_yaml(args.file)
1029             if args.item not in create_dict:
1030                 create_dict = {args.item: create_dict}
1031         else:
1032             create_dict = {args.item:{}}
1033         if args.name:
1034             create_dict[args.item]['name'] = args.name
1035         #if args.description:
1036         #    create_dict[args.item]['description'] = args.description
1037         if args.item=="vim-net":
1038             if args.bind_net:
1039                 create_dict[args.item]['bind_net'] = args.bind_net
1040             if args.bind_type:
1041                 create_dict[args.item]['bind_type'] = args.bind_type
1042             if args.shared:
1043                 create_dict[args.item]['shared'] = args.shared
1044         if "name" not in create_dict[args.item]:
1045             print "You must provide a name in the descriptor file or with the --name option"
1046             return
1047         payload_req = yaml.safe_dump(create_dict, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
1048         logger.debug("openmano request: %s", payload_req)
1049         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, mano_tenant, datacenter, args.item)
1050         mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
1051         logger.debug("openmano response: %s", mano_response.text )
1052         if args.verbose==None:
1053             args.verbose=0
1054         return _print_verbose(mano_response, args.verbose)
1055
1056
1057 def datacenter_net_action(args):
1058     if args.action == "net-update":
1059         print "This command is deprecated, use 'openmano datacenter-netmap-delete --all' and 'openmano datacenter-netmap-import' instead!!!"
1060         print
1061         args.action = "netmap-delete"
1062         args.netmap = None
1063         args.all = True
1064         r = datacenter_netmap_action(args)
1065         if r == 0:
1066             args.force = True
1067             args.action = "netmap-import"
1068             r = datacenter_netmap_action(args)
1069         return r
1070
1071     if args.action == "net-edit":
1072         args.netmap = args.net
1073         args.name = None
1074     elif args.action == "net-list":
1075         args.netmap = None
1076     elif args.action == "net-delete":
1077         args.netmap = args.net
1078         args.all = False
1079           
1080     args.action = "netmap" + args.action[3:]
1081     args.vim_name=None
1082     args.vim_id=None
1083     print "This command is deprecated, use 'openmano datacenter-%s' instead!!!" % args.action
1084     print
1085     return datacenter_netmap_action(args)
1086
1087 def datacenter_netmap_action(args):
1088     tenant = _get_tenant()
1089     datacenter = _get_datacenter(args.datacenter, tenant)
1090     #print "datacenter_netmap_action",args
1091     payload_req = None
1092     if args.verbose==None:
1093         args.verbose=0
1094     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1095     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/netmaps" %(mano_host, mano_port, tenant, datacenter)
1096         
1097     if args.action=="netmap-list":
1098         if args.netmap:
1099             URLrequest += "/" + args.netmap
1100             args.verbose += 1
1101         mano_response = requests.get(URLrequest)
1102             
1103     elif args.action=="netmap-delete":
1104         if args.netmap and args.all:
1105             print "you can not use a netmap name and the option --all at the same time"
1106             return 1
1107         if args.netmap:
1108             force_text= "Delete default netmap '%s' from datacenter '%s' (y/N)? " % (args.netmap, datacenter)
1109             URLrequest += "/" + args.netmap
1110         elif args.all: 
1111             force_text="Delete all default netmaps from datacenter '%s' (y/N)? " % (datacenter)
1112         else:
1113             print "you must specify a netmap name or the option --all"
1114             return 1
1115         if not args.force:
1116             r = raw_input(force_text)
1117             if  len(r)>0  and r[0].lower()=="y":
1118                 pass
1119             else:
1120                 return 0
1121         mano_response = requests.delete(URLrequest, headers=headers_req)
1122     elif args.action=="netmap-import":
1123         if not args.force:
1124             r = raw_input("Create all the available networks from datacenter '%s' as default netmaps (y/N)? " % (datacenter))
1125             if  len(r)>0  and r[0].lower()=="y":
1126                 pass
1127             else:
1128                 return 0
1129         URLrequest += "/upload"
1130         mano_response = requests.post(URLrequest, headers=headers_req)
1131     elif args.action=="netmap-edit" or args.action=="netmap-create":
1132         if args.file:
1133             payload = _load_file_or_yaml(args.file)
1134         else:
1135             payload = {}
1136         if "netmap" not in payload:
1137             payload = {"netmap": payload}
1138         if args.name:
1139             payload["netmap"]["name"] = args.name
1140         if args.vim_id:
1141             payload["netmap"]["vim_id"] = args.vim_id
1142         if args.action=="netmap-create" and args.vim_name:
1143             payload["netmap"]["vim_name"] = args.vim_name
1144         payload_req = json.dumps(payload)
1145         logger.debug("openmano request: %s", payload_req)
1146         
1147         if args.action=="netmap-edit" and not args.force:
1148             if len(payload["netmap"]) == 0:
1149                 print "You must supply some parameter to edit"
1150                 return 1
1151             r = raw_input("Edit default netmap '%s' from datacenter '%s' (y/N)? " % (args.netmap, datacenter))
1152             if  len(r)>0  and r[0].lower()=="y":
1153                 pass
1154             else:
1155                 return 0
1156             URLrequest += "/" + args.netmap
1157             mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1158         else: #netmap-create
1159             if "vim_name" not in payload["netmap"] and "vim_id" not in payload["netmap"]:
1160                 print "You must supply either --vim-id or --vim-name option; or include one of them in the file descriptor"
1161                 return 1
1162             mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
1163
1164     logger.debug("openmano response: %s", mano_response.text )
1165     return _print_verbose(mano_response, args.verbose)
1166
1167 def element_edit(args):
1168     element = _get_item_uuid(args.element, args.name)
1169     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1170     URLrequest = "http://%s:%s/openmano/%s/%s" %(mano_host, mano_port, args.element, element)
1171     payload=_load_file_or_yaml(args.file)
1172     if args.element[:-1] not in payload:
1173         payload = {args.element[:-1]: payload }
1174     payload_req = json.dumps(payload)
1175     
1176     #print payload_req
1177     if not args.force or (args.name==None and args.filer==None):
1178         r = raw_input(" Edit " + args.element[:-1] + " " + args.name + " (y/N)? ")
1179         if  len(r)>0  and r[0].lower()=="y":
1180             pass
1181         else:
1182             return 0
1183     logger.debug("openmano request: %s", payload_req)
1184     mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1185     logger.debug("openmano response: %s", mano_response.text )
1186     if args.verbose==None:
1187         args.verbose=0
1188     if args.name!=None:
1189         args.verbose += 1
1190     return _print_verbose(mano_response, args.verbose)
1191
1192
1193 global mano_host
1194 global mano_port
1195 global mano_tenant
1196
1197 if __name__=="__main__":
1198     
1199     mano_tenant = os.getenv('OPENMANO_TENANT', None)
1200     mano_host = os.getenv('OPENMANO_HOST',"localhost")
1201     mano_port = os.getenv('OPENMANO_PORT',"9090")
1202     mano_datacenter = os.getenv('OPENMANO_DATACENTER',None)
1203     
1204     main_parser = ThrowingArgumentParser(description='User program to interact with OPENMANO-SERVER (openmanod)')
1205     main_parser.add_argument('--version', action='version', version='%(prog)s ' + __version__ )
1206     
1207     subparsers = main_parser.add_subparsers(help='commands')
1208     
1209     parent_parser = argparse.ArgumentParser(add_help=False)
1210     parent_parser.add_argument('--verbose', '-v', action='count', help="increase verbosity level. Use several times")
1211     parent_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
1212
1213     config_parser = subparsers.add_parser('config', parents=[parent_parser], help="prints configuration values")
1214     config_parser.add_argument("-n", action="store_true", help="resolves tenant and datacenter names")
1215     config_parser.set_defaults(func=config)
1216
1217     vnf_create_parser = subparsers.add_parser('vnf-create', parents=[parent_parser], help="adds a vnf into the catalogue")
1218     vnf_create_parser.add_argument("file", action="store", help="location of the JSON file describing the VNF").completer = FilesCompleter
1219     vnf_create_parser.add_argument("--name", action="store", help="name of the VNF (if it exists in the VNF descriptor, it is overwritten)")
1220     vnf_create_parser.add_argument("--description", action="store", help="description of the VNF (if it exists in the VNF descriptor, it is overwritten)")
1221     vnf_create_parser.add_argument("--image-path", action="store",  help="change image path locations (overwritten)")
1222     vnf_create_parser.add_argument("--image-name", action="store",  help="change image name (overwritten)")
1223     vnf_create_parser.add_argument("--image-checksum", action="store",  help="change image checksum (overwritten)")
1224     vnf_create_parser.set_defaults(func=vnf_create)
1225
1226     vnf_list_parser = subparsers.add_parser('vnf-list', parents=[parent_parser], help="lists information about a vnf")
1227     vnf_list_parser.add_argument("name", nargs='?', help="name of the VNF")
1228     vnf_list_parser.add_argument("-a", "--all", action="store_true", help="shows all vnfs, not only the owned or public ones")
1229     #vnf_list_parser.add_argument('--descriptor', help="prints the VNF descriptor", action="store_true")
1230     vnf_list_parser.set_defaults(func=vnf_list)
1231     
1232     vnf_delete_parser = subparsers.add_parser('vnf-delete', parents=[parent_parser], help="deletes a vnf from the catalogue")
1233     vnf_delete_parser.add_argument("name", action="store", help="name or uuid of the VNF to be deleted")
1234     vnf_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1235     vnf_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1236     vnf_delete_parser.set_defaults(func=vnf_delete)
1237     
1238     scenario_create_parser = subparsers.add_parser('scenario-create', parents=[parent_parser], help="adds a scenario into the OPENMANO DB")
1239     scenario_create_parser.add_argument("file", action="store", help="location of the YAML file describing the scenario").completer = FilesCompleter
1240     scenario_create_parser.add_argument("--name", action="store", help="name of the scenario (if it exists in the YAML scenario, it is overwritten)")
1241     scenario_create_parser.add_argument("--description", action="store", help="description of the scenario (if it exists in the YAML scenario, it is overwritten)")
1242     scenario_create_parser.set_defaults(func=scenario_create)
1243
1244     scenario_list_parser = subparsers.add_parser('scenario-list', parents=[parent_parser], help="lists information about a scenario")
1245     scenario_list_parser.add_argument("name", nargs='?', help="name of the scenario")
1246     #scenario_list_parser.add_argument('--descriptor', help="prints the scenario descriptor", action="store_true")
1247     scenario_list_parser.add_argument("-a", "--all", action="store_true", help="shows all scenarios, not only the owned or public ones")
1248     scenario_list_parser.set_defaults(func=scenario_list)
1249     
1250     scenario_delete_parser = subparsers.add_parser('scenario-delete', parents=[parent_parser], help="deletes a scenario from the OPENMANO DB")
1251     scenario_delete_parser.add_argument("name", action="store", help="name or uuid of the scenario to be deleted")
1252     scenario_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1253     scenario_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1254     scenario_delete_parser.set_defaults(func=scenario_delete)
1255
1256     scenario_deploy_parser = subparsers.add_parser('scenario-deploy', parents=[parent_parser], help="deploys a scenario")
1257     scenario_deploy_parser.add_argument("scenario", action="store", help="name or uuid of the scenario to be deployed")
1258     scenario_deploy_parser.add_argument("name", action="store", help="name of the instance")
1259     scenario_deploy_parser.add_argument("--nostart", action="store_true", help="does not start the vms, just reserve resources")
1260     scenario_deploy_parser.add_argument("--datacenter", action="store", help="specifies the datacenter. Needed if several datacenters are available")
1261     scenario_deploy_parser.add_argument("--description", action="store", help="description of the instance")
1262     scenario_deploy_parser.set_defaults(func=scenario_deploy)
1263     
1264     scenario_deploy_parser = subparsers.add_parser('scenario-verify', help="verifies if a scenario can be deployed (deploys it and deletes it)")
1265     scenario_deploy_parser.add_argument("scenario", action="store", help="name or uuid of the scenario to be verified")
1266     scenario_deploy_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
1267     scenario_deploy_parser.set_defaults(func=scenario_verify)
1268     
1269     instance_scenario_create_parser = subparsers.add_parser('instance-scenario-create', parents=[parent_parser], help="deploys a scenario")
1270     instance_scenario_create_parser.add_argument("file", nargs='?', help="descriptor of the instance. Must be a file or yaml/json text")
1271     instance_scenario_create_parser.add_argument("--scenario", action="store", help="name or uuid of the scenario to be deployed")
1272     instance_scenario_create_parser.add_argument("--name", action="store", help="name of the instance")
1273     instance_scenario_create_parser.add_argument("--nostart", action="store_true", help="does not start the vms, just reserve resources")
1274     instance_scenario_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter. Needed if several datacenters are available")
1275     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")
1276     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")
1277     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")
1278     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")
1279     instance_scenario_create_parser.add_argument("--description", action="store", help="description of the instance")
1280     instance_scenario_create_parser.set_defaults(func=instance_create)
1281
1282     instance_scenario_list_parser = subparsers.add_parser('instance-scenario-list', parents=[parent_parser], help="lists information about a scenario instance")
1283     instance_scenario_list_parser.add_argument("name", nargs='?', help="name of the scenario instance")
1284     instance_scenario_list_parser.add_argument("-a", "--all", action="store_true", help="shows all instance-scenarios, not only the owned")
1285     instance_scenario_list_parser.set_defaults(func=instance_scenario_list)
1286
1287     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)")
1288     instance_scenario_delete_parser.add_argument("name", action="store", help="name or uuid of the scenario instance to be deleted")
1289     instance_scenario_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1290     instance_scenario_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1291     instance_scenario_delete_parser.set_defaults(func=instance_scenario_delete)
1292     
1293     instance_scenario_action_parser = subparsers.add_parser('instance-scenario-action', parents=[parent_parser], help="invoke an action over part or the whole scenario instance")
1294     instance_scenario_action_parser.add_argument("name", action="store", help="name or uuid of the scenario instance")
1295     instance_scenario_action_parser.add_argument("action", action="store", type=str, \
1296             choices=["start","pause","resume","shutoff","shutdown","forceOff","rebuild","reboot", "console"],\
1297             help="action to send")
1298     instance_scenario_action_parser.add_argument("param", nargs='?', help="addional param of the action. e.g. console type (novnc, ...), reboot type (TODO)")
1299     instance_scenario_action_parser.add_argument("--vnf", action="append", help="VNF to act on (can use several entries)")
1300     instance_scenario_action_parser.add_argument("--vm", action="append", help="VM to act on (can use several entries)")
1301     instance_scenario_action_parser.set_defaults(func=instance_scenario_action)
1302
1303     #instance_scenario_status_parser = subparsers.add_parser('instance-scenario-status', help="show the status of a scenario instance")
1304     #instance_scenario_status_parser.add_argument("name", action="store", help="name or uuid of the scenario instance")
1305     #instance_scenario_status_parser.set_defaults(func=instance_scenario_status)
1306     
1307     tenant_create_parser = subparsers.add_parser('tenant-create', parents=[parent_parser], help="creates a new tenant")
1308     tenant_create_parser.add_argument("name", action="store", help="name for the tenant")
1309     tenant_create_parser.add_argument("--description", action="store", help="description of the tenant")
1310     tenant_create_parser.set_defaults(func=tenant_create)
1311
1312     tenant_delete_parser = subparsers.add_parser('tenant-delete', parents=[parent_parser], help="deletes a tenant from the catalogue")
1313     tenant_delete_parser.add_argument("name", action="store", help="name or uuid of the tenant to be deleted")
1314     tenant_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1315     tenant_delete_parser.set_defaults(func=tenant_delete)
1316
1317     tenant_list_parser = subparsers.add_parser('tenant-list', parents=[parent_parser], help="lists information about a tenant")
1318     tenant_list_parser.add_argument("name", nargs='?', help="name or uuid of the tenant")
1319     tenant_list_parser.set_defaults(func=tenant_list)
1320
1321     item_list=('tenant','datacenter') #put tenant before so that help appear in order
1322     for item in item_list:
1323         element_edit_parser = subparsers.add_parser(item+'-edit', parents=[parent_parser], help="edits one "+item)
1324         element_edit_parser.add_argument("name", help="name or uuid of the "+item)
1325         element_edit_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter
1326         element_edit_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1327         element_edit_parser.set_defaults(func=element_edit, element=item + 's')
1328
1329     datacenter_create_parser = subparsers.add_parser('datacenter-create', parents=[parent_parser], help="creates a new datacenter")
1330     datacenter_create_parser.add_argument("name", action="store", help="name for the datacenter")
1331     datacenter_create_parser.add_argument("url", action="store", help="url for the datacenter")
1332     datacenter_create_parser.add_argument("--url_admin", action="store", help="url for administration for the datacenter")
1333     datacenter_create_parser.add_argument("--type", action="store", help="datacenter type: openstack or openvim (default)")
1334     datacenter_create_parser.add_argument("--config", action="store", help="aditional configuration in json/yaml format")
1335     datacenter_create_parser.add_argument("--description", action="store", help="description of the datacenter")
1336     datacenter_create_parser.set_defaults(func=datacenter_create)
1337
1338     datacenter_delete_parser = subparsers.add_parser('datacenter-delete', parents=[parent_parser], help="deletes a datacenter from the catalogue")
1339     datacenter_delete_parser.add_argument("name", action="store", help="name or uuid of the datacenter to be deleted")
1340     datacenter_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1341     datacenter_delete_parser.set_defaults(func=datacenter_delete)
1342
1343     datacenter_list_parser = subparsers.add_parser('datacenter-list', parents=[parent_parser], help="lists information about a datacenter")
1344     datacenter_list_parser.add_argument("name", nargs='?', help="name or uuid of the datacenter")
1345     datacenter_list_parser.add_argument("-a", "--all", action="store_true", help="shows all datacenters, not only datacenters attached to tenant")
1346     datacenter_list_parser.set_defaults(func=datacenter_list)
1347
1348     datacenter_attach_parser = subparsers.add_parser('datacenter-attach', parents=[parent_parser], help="associates a datacenter to the operating tenant")
1349     datacenter_attach_parser.add_argument("name", help="name or uuid of the datacenter")
1350     datacenter_attach_parser.add_argument('--vim-tenant-id', action='store', help="specify a datacenter tenant to use. A new one is created by default")
1351     datacenter_attach_parser.add_argument('--vim-tenant-name', action='store', help="specify a datacenter tenant name.")
1352     datacenter_attach_parser.add_argument("--user", action="store", help="user credentials for the datacenter")
1353     datacenter_attach_parser.add_argument("--password", action="store", help="password credentials for the datacenter")
1354     datacenter_attach_parser.add_argument("--config", action="store", help="aditional configuration in json/yaml format")
1355     datacenter_attach_parser.set_defaults(func=datacenter_attach)
1356
1357     datacenter_detach_parser = subparsers.add_parser('datacenter-detach', parents=[parent_parser], help="removes the association between a datacenter and the operating tenant")
1358     datacenter_detach_parser.add_argument("name", help="name or uuid of the datacenter")
1359     datacenter_detach_parser.add_argument("-a", "--all", action="store_true", help="removes all associations from this datacenter")
1360     datacenter_detach_parser.set_defaults(func=datacenter_detach)
1361
1362
1363     action_dict={'net-update': 'retrieves external networks from datacenter',
1364                  'net-edit': 'edits an external network',
1365                  'net-delete': 'deletes an external network',
1366                  'net-list': 'lists external networks from a datacenter'
1367                  }
1368     for item in action_dict:
1369         datacenter_action_parser = subparsers.add_parser('datacenter-'+item, parents=[parent_parser], help=action_dict[item])
1370         datacenter_action_parser.add_argument("datacenter", help="name or uuid of the datacenter")
1371         if item=='net-edit' or item=='net-delete':
1372             datacenter_action_parser.add_argument("net", help="name or uuid of the datacenter net")
1373         if item=='net-edit':
1374             datacenter_action_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter
1375         if item!='net-list':
1376             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1377         datacenter_action_parser.set_defaults(func=datacenter_net_action, action=item)
1378
1379
1380     action_dict={'netmap-import': 'create network senario netmap base on the datacenter networks',
1381                  'netmap-create': 'create a new network senario netmap',
1382                  'netmap-edit':   'edit name of a network senario netmap',
1383                  'netmap-delete': 'deletes a network scenario netmap (--all for clearing all)',
1384                  'netmap-list':   'list/show network scenario netmaps'
1385                  }
1386     for item in action_dict:
1387         datacenter_action_parser = subparsers.add_parser('datacenter-'+item, parents=[parent_parser], help=action_dict[item])
1388         datacenter_action_parser.add_argument("--datacenter", help="name or uuid of the datacenter")
1389         #if item=='net-add':
1390         #    datacenter_action_parser.add_argument("net", help="name of the network")
1391         if item=='netmap-delete':
1392             datacenter_action_parser.add_argument("netmap", nargs='?',help="name or uuid of the datacenter netmap to delete")
1393             datacenter_action_parser.add_argument("--all", action="store_true", help="delete all netmap of this datacenter")
1394             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1395         if item=='netmap-edit':
1396             datacenter_action_parser.add_argument("netmap", help="name or uuid of the datacenter netmap do edit")
1397             datacenter_action_parser.add_argument("file", nargs='?', help="json/yaml text or file with the changes").completer = FilesCompleter
1398             datacenter_action_parser.add_argument("--name", action='store', help="name to assign to the datacenter netmap")
1399             datacenter_action_parser.add_argument('--vim-id', action='store', help="specify vim network uuid")
1400             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1401         if item=='netmap-list':
1402             datacenter_action_parser.add_argument("netmap", nargs='?',help="name or uuid of the datacenter netmap to show")
1403         if item=='netmap-create':
1404             datacenter_action_parser.add_argument("file", nargs='?', help="json/yaml text or file descriptor with the changes").completer = FilesCompleter
1405             datacenter_action_parser.add_argument("--name", action='store', help="name to assign to the datacenter netmap, by default same as vim-name")
1406             datacenter_action_parser.add_argument('--vim-id', action='store', help="specify vim network uuid")
1407             datacenter_action_parser.add_argument('--vim-name', action='store', help="specify vim network name")
1408         if item=='netmap-import':
1409             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1410         datacenter_action_parser.set_defaults(func=datacenter_netmap_action, action=item)
1411     
1412     for item in ("network", "tenant"):
1413         if item=="network":
1414             commnad_name = 'vim-net'
1415         else:
1416             commnad_name = 'vim-'+item
1417         vim_item_list_parser = subparsers.add_parser(commnad_name + '-list', parents=[parent_parser], help="list the vim " + item + "s")
1418         vim_item_list_parser.add_argument("name", nargs='?', help="name or uuid of the " + item + "s")
1419         vim_item_list_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1420         vim_item_list_parser.set_defaults(func=vim_action, item=item, action="list")
1421
1422         vim_item_del_parser = subparsers.add_parser(commnad_name + '-delete', parents=[parent_parser], help="list the vim " + item + "s")
1423         vim_item_del_parser.add_argument("name", help="name or uuid of the " + item + "s")
1424         vim_item_del_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1425         vim_item_del_parser.set_defaults(func=vim_action, item=item, action="delete")
1426
1427         vim_item_create_parser = subparsers.add_parser(commnad_name + '-create', parents=[parent_parser], help="create a "+item+" at vim")
1428         vim_item_create_parser.add_argument("file", nargs='?', help="descriptor of the %s. Must be a file or yaml/json text" % item).completer = FilesCompleter
1429         vim_item_create_parser.add_argument("--name", action="store", help="name of the %s" % item  )
1430         vim_item_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1431         if item=="network":
1432             vim_item_create_parser.add_argument("--type", action="store", help="type of network, data, ptp, bridge")
1433             vim_item_create_parser.add_argument("--shared", action="store_true", help="Private or shared")
1434             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>'")
1435         else:
1436             vim_item_create_parser.add_argument("--description", action="store", help="description of the %s" % item)
1437         vim_item_create_parser.set_defaults(func=vim_action, item=item, action="create")
1438
1439     argcomplete.autocomplete(main_parser)
1440     
1441     try:
1442         args = main_parser.parse_args()
1443         #logging info
1444         level = logging.CRITICAL
1445         streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1446         if "debug" in args and args.debug:
1447             level = logging.DEBUG
1448         logging.basicConfig(format=streamformat, level= level)
1449         logger = logging.getLogger('mano')
1450         logger.setLevel(level)
1451         result = args.func(args)
1452         if result == None:
1453             result = 0
1454         #for some reason it fails if call exit inside try instance. Need to call exit at the end !?
1455     except (requests.exceptions.ConnectionError):
1456         print "Connection error: not possible to contact OPENMANO-SERVER (openmanod)"
1457         result = -2
1458     except (KeyboardInterrupt):
1459         print 'Exiting openmano'
1460         result = -3
1461     except (SystemExit, ArgumentParserError):
1462         result = -4
1463     except OpenmanoCLIError as e:
1464         print str(e)
1465         result = -5
1466     
1467     #print result
1468     exit(result)
1469