OSM IM consumed, changes in devops-related files: Makefile, Dockerfile, setup, new...
[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, Pablo Montes"
30 __date__ = "$09-oct-2014 09:09:48$"
31 __version__ = "0.4.15-r525"
32 version_date = "Jul 2017"
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         uuid = None
124         for content in content_list:
125             if "uuid" in content:
126                 uuid = content['uuid']
127             elif "id" in content:
128                 uuid = content['id']
129             elif "vim_id" in content:
130                 uuid = content['vim_id']
131             name = content.get('name');
132             if not uuid:
133                 uuid = ""
134             if not name:
135                 name = ""
136             myoutput = "%s %s" %(uuid.ljust(38),name.ljust(20))
137             if content.get("status"):
138                 myoutput += " " + content['status'].ljust(20)
139             elif "enabled" in content and not content["enabled"]:
140                 myoutput += " enabled=False".ljust(20)
141             if verbose_level >=1:
142                 if content.get('created_at'):
143                     myoutput += " " + content['created_at'].ljust(20)
144                 if content.get('sdn_attached_ports'):
145                     #myoutput += " " + str(content['sdn_attached_ports']).ljust(20)
146                     myoutput += "\nsdn_attached_ports:\n" + yaml.safe_dump(content['sdn_attached_ports'], indent=4, default_flow_style=False)
147                 if verbose_level >=2:
148                     new_line='\n'
149                     if content.get('type'):
150                         myoutput += new_line + "  Type: " + content['type'].ljust(29)
151                         new_line=''
152                     if content.get('description'):
153                         myoutput += new_line + "  Description: " + content['description'].ljust(20)
154             print myoutput
155     else:
156         print content['error']['description']
157     return result
158
159 def parser_json_yaml(file_name):
160     try:
161         f = file(file_name, "r")
162         text = f.read()
163         f.close()
164     except Exception as e:
165         return (False, str(e))
166            
167     #Read and parse file
168     if file_name[-5:]=='.yaml' or file_name[-4:]=='.yml' or (file_name[-5:]!='.json' and '\t' not in text):
169         try:
170             config = yaml.load(text)
171         except yaml.YAMLError as exc:
172             error_pos = ""
173             if hasattr(exc, 'problem_mark'):
174                 mark = exc.problem_mark
175                 error_pos = " at line:%s column:%s" % (mark.line+1, mark.column+1)
176             return (False, "Error loading file '"+file_name+"' yaml format error" + error_pos)
177     else: #json
178         try:
179             config = json.loads(text) 
180         except Exception as e:
181             return (False, "Error loading file '"+file_name+"' json format error " + str(e) )
182
183     return True, config
184
185 def _load_file_or_yaml(content):
186     '''
187     'content' can be or a yaml/json file or a text containing a yaml/json text format
188     This function autodetect, trying to load and parse the file,
189     if fails trying to parse the 'content' text
190     Returns the dictionary once parsed, or print an error and finish the program
191     '''
192     #Check config file exists
193     if os.path.isfile(content):
194         r,payload = parser_json_yaml(content)
195         if not r:
196             print payload
197             exit(-1)
198     elif "{" in content or ":" in content:
199         try:
200             payload = yaml.load(content)
201         except yaml.YAMLError as exc:
202             error_pos = ""
203             if hasattr(exc, 'problem_mark'):
204                 mark = exc.problem_mark
205                 error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1)
206             print "Error loading yaml/json text"+error_pos
207             exit (-1)
208     else:
209         print "'%s' is neither a valid file nor a yaml/json content" % content
210         exit(-1)
211     return payload
212
213 def _get_item_uuid(item, item_name_id, tenant=None):
214     if tenant:
215         URLrequest = "http://%s:%s/openmano/%s/%s" %(mano_host, mano_port, tenant, item)
216     else:
217         URLrequest = "http://%s:%s/openmano/%s" %(mano_host, mano_port, item)
218     mano_response = requests.get(URLrequest)
219     logger.debug("openmano response: %s", mano_response.text )
220     content = mano_response.json()
221     #print content
222     found = 0
223     for i in content[item]:
224         if i["uuid"] == item_name_id:
225             return item_name_id
226         if i["name"] == item_name_id:
227             uuid = i["uuid"]
228             found += 1
229     if found == 0:
230         raise OpenmanoCLIError("No %s found with name/uuid '%s'" %(item[:-1], item_name_id))
231     elif found > 1:
232         raise OpenmanoCLIError("%d %s found with name '%s'. uuid must be used" %(found, item, item_name_id))
233     return uuid
234
235 # def check_valid_uuid(uuid):
236 #     id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
237 #     try:
238 #         js_v(uuid, id_schema)
239 #         return True
240 #     except js_e.ValidationError:
241 #         return False
242     
243 def _get_tenant(tenant_name_id = None):
244     if not tenant_name_id:
245         tenant_name_id = mano_tenant
246         if not mano_tenant:
247             raise OpenmanoCLIError("'OPENMANO_TENANT' environment variable is not set")
248     return _get_item_uuid("tenants", tenant_name_id)
249
250 def _get_datacenter(datacenter_name_id = None, tenant = "any"):
251     if not datacenter_name_id:
252         datacenter_name_id = mano_datacenter
253         if not datacenter_name_id:
254             raise OpenmanoCLIError("neither 'OPENMANO_DATACENTER' environment variable is set nor --datacenter option is used")
255     return _get_item_uuid("datacenters", datacenter_name_id, tenant)
256
257 def vnf_create(args):
258     #print "vnf-create",args
259     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
260     tenant = _get_tenant()
261     myvnf = _load_file_or_yaml(args.file)
262
263     if args.name or args.description or args.image_path or args.image_name or args.image_checksum:
264         #print args.name
265         try:
266             if args.name:
267                 myvnf['vnf']['name'] = args.name
268             if args.description:
269                 myvnf['vnf']['description'] = args.description
270             if args.image_path:
271                 index=0
272                 for image_path_ in args.image_path.split(","):
273                     #print "image-path", image_path_
274                     myvnf['vnf']['VNFC'][index]['VNFC image']=image_path_
275                     if "image name" in myvnf['vnf']['VNFC'][index]:
276                         del myvnf['vnf']['VNFC'][index]["image name"]
277                     if "image checksum" in myvnf['vnf']['VNFC'][index]:
278                         del myvnf['vnf']['VNFC'][index]["image checksum"]
279                     index=index+1
280             if args.image_name: #image name precedes if both are supplied
281                 index=0
282                 for image_name_ in args.image_name.split(","):
283                     myvnf['vnf']['VNFC'][index]['image name']=image_name_
284                     if "VNFC image" in myvnf['vnf']['VNFC'][index]:
285                         del myvnf['vnf']['VNFC'][index]["VNFC image"]
286                     index=index+1
287             if args.image_checksum:
288                 index=0
289                 for image_checksum_ in args.image_checksum.split(","):
290                     myvnf['vnf']['VNFC'][index]['image checksum']=image_checksum_
291                     index=index+1
292         except (KeyError, TypeError), e:
293             if str(e)=='vnf':           error_pos= "missing field 'vnf'"
294             elif str(e)=='name':        error_pos= "missing field  'vnf':'name'"
295             elif str(e)=='description': error_pos= "missing field  'vnf':'description'"
296             elif str(e)=='VNFC':        error_pos= "missing field  'vnf':'VNFC'"
297             elif str(e)==str(index):    error_pos= "field  'vnf':'VNFC' must be an array"
298             elif str(e)=='VNFC image':  error_pos= "missing field 'vnf':'VNFC'['VNFC image']"
299             elif str(e)=='image name':  error_pos= "missing field 'vnf':'VNFC'['image name']"
300             elif str(e)=='image checksum':  error_pos= "missing field 'vnf':'VNFC'['image checksum']"
301             else:                       error_pos="wrong format"
302             print "Wrong VNF descriptor: " + error_pos
303             return -1 
304     payload_req = json.dumps(myvnf)
305         
306     #print payload_req
307         
308     URLrequest = "http://%s:%s/openmano/%s/vnfs" %(mano_host, mano_port, tenant)
309     logger.debug("openmano request: %s", payload_req)
310     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
311     logger.debug("openmano response: %s", mano_response.text )
312
313     return _print_verbose(mano_response, args.verbose)
314
315 def vnf_list(args):
316     #print "vnf-list",args
317     if args.all:
318         tenant = "any"
319     else:
320         tenant = _get_tenant()
321     if args.name:
322         toshow = _get_item_uuid("vnfs", args.name, tenant)
323         URLrequest = "http://%s:%s/openmano/%s/vnfs/%s" %(mano_host, mano_port, tenant, toshow)
324     else:
325         URLrequest = "http://%s:%s/openmano/%s/vnfs" %(mano_host, mano_port, tenant)
326     mano_response = requests.get(URLrequest)
327     logger.debug("openmano response: %s", mano_response.text )
328     content = mano_response.json()
329     #print json.dumps(content, indent=4)
330     if args.verbose==None:
331         args.verbose=0
332     result = 0 if mano_response.status_code==200 else mano_response.status_code
333     if mano_response.status_code == 200:
334         if not args.name:
335             if args.verbose >= 3:
336                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
337                 return result
338             if len(content['vnfs']) == 0:
339                 print "No VNFs were found."
340                 return 404 #HTTP_Not_Found
341             for vnf in content['vnfs']:
342                 myoutput = "%s %s" %(vnf['uuid'].ljust(38),vnf['name'].ljust(20))
343                 if args.verbose >=1:
344                     myoutput = "%s %s" %(myoutput, vnf['created_at'].ljust(20))
345                 print myoutput
346                 if args.verbose >=2:
347                     print "  Description: %s" %vnf['description']
348                     print "  VNF descriptor file: %s" %vnf['path']
349         else:
350             if args.verbose:
351                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
352                 return result
353             vnf = content['vnf']
354             print "%s %s %s" %(vnf['uuid'].ljust(38),vnf['name'].ljust(20), vnf['created_at'].ljust(20))
355             print "  Description: %s" %vnf['description']
356             #print "  VNF descriptor file: %s" %vnf['path']
357             print "    VMs:"
358             for vm in vnf['VNFC']:
359                 #print "    %s %s %s" %(vm['name'].ljust(20), vm['uuid'].ljust(38), vm['description'].ljust(30))
360                 print "        %s %s" %(vm['name'].ljust(20), vm['description'])
361             if len(vnf['nets'])>0:
362                 print "    Internal nets:"
363                 for net in vnf['nets']:
364                     print "        %s %s" %(net['name'].ljust(20), net['description'])
365             if len(vnf['external-connections'])>0:
366                 print "    External interfaces:"
367                 for interface in vnf['external-connections']:
368                     print "        %s %s %s %s" %(interface['external_name'].ljust(20), interface['vm_name'].ljust(20), interface['internal_name'].ljust(20), \
369                                                   interface.get('vpci',"").ljust(14))
370     else:
371         print content['error']['description']
372         if args.verbose:
373             print yaml.safe_dump(content, indent=4, default_flow_style=False)
374     return result
375
376 def vnf_delete(args):
377     #print "vnf-delete",args
378     if args.all:
379         tenant = "any"
380     else:
381         tenant = _get_tenant()
382     todelete = _get_item_uuid("vnfs", args.name, tenant=tenant)
383     if not args.force:
384         r = raw_input("Delete VNF %s (y/N)? " %(todelete))
385         if  not (len(r)>0  and r[0].lower()=="y"):
386             return 0
387     URLrequest = "http://%s:%s/openmano/%s/vnfs/%s" %(mano_host, mano_port, tenant, todelete)
388     mano_response = requests.delete(URLrequest)
389     logger.debug("openmano response: %s", mano_response.text )
390     result = 0 if mano_response.status_code==200 else mano_response.status_code
391     content = mano_response.json()
392     #print json.dumps(content, indent=4)
393     if mano_response.status_code == 200:
394         print content['result']
395     else:
396         print content['error']['description']
397     return result
398
399 def scenario_create(args):
400     #print "scenario-create",args
401     tenant = _get_tenant()
402     headers_req = {'content-type': 'application/yaml'}
403     myscenario = _load_file_or_yaml(args.file)
404
405     if args.name:
406         myscenario['name'] = args.name
407     if args.description:
408         myscenario['description'] = args.description
409     payload_req = yaml.safe_dump(myscenario, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
410     
411     #print payload_req
412         
413     URLrequest = "http://%s:%s/openmano/%s/scenarios" %(mano_host, mano_port, tenant)
414     logger.debug("openmano request: %s", payload_req)
415     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
416     logger.debug("openmano response: %s", mano_response.text )
417     return _print_verbose(mano_response, args.verbose)
418
419 def scenario_list(args):
420     #print "scenario-list",args
421     if args.all:
422         tenant = "any"
423     else:
424         tenant = _get_tenant()
425     if args.name:
426         toshow = _get_item_uuid("scenarios", args.name, tenant)
427         URLrequest = "http://%s:%s/openmano/%s/scenarios/%s" %(mano_host, mano_port, tenant, toshow)
428     else:
429         URLrequest = "http://%s:%s/openmano/%s/scenarios" %(mano_host, mano_port, tenant)
430     mano_response = requests.get(URLrequest)
431     logger.debug("openmano response: %s", mano_response.text )
432     content = mano_response.json()
433     #print json.dumps(content, indent=4)
434     if args.verbose==None:
435         args.verbose=0
436
437     result = 0 if mano_response.status_code==200 else mano_response.status_code
438     if mano_response.status_code == 200:
439         if not args.name:
440             if args.verbose >= 3:
441                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
442                 return result
443             if len(content['scenarios']) == 0:
444                 print "No scenarios were found."
445                 return 404 #HTTP_Not_Found
446             for scenario in content['scenarios']:
447                 myoutput = "%s %s" %(scenario['uuid'].ljust(38),scenario['name'].ljust(20))
448                 if args.verbose >=1:
449                     myoutput = "%s %s" %(myoutput, scenario['created_at'].ljust(20))
450                 print myoutput
451                 if args.verbose >=2:
452                     print "  Description: %s" %scenario['description']
453         else:
454             if args.verbose:
455                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
456                 return result
457             scenario = content['scenario']
458             myoutput = "%s %s %s" %(scenario['uuid'].ljust(38),scenario['name'].ljust(20), scenario['created_at'].ljust(20))
459             print myoutput
460             print "  Description: %s" %scenario['description']
461             print "    VNFs:"
462             for vnf in scenario['vnfs']:
463                 print "        %s %s %s" %(vnf['name'].ljust(20), vnf['vnf_id'].ljust(38), vnf['description'])
464             if len(scenario['nets'])>0:
465                 print "    Internal nets:"
466                 for net in scenario['nets']:
467                     if net['description'] is None:   #if description does not exist, description is "-". Valid for external and internal nets.
468                         net['description'] = '-' 
469                     if not net['external']:
470                         print "        %s %s %s" %(net['name'].ljust(20), net['uuid'].ljust(38), net['description'].ljust(30))
471                 print "    External nets:"
472                 for net in scenario['nets']:
473                     if net['external']:
474                         print "        %s %s %s vim-id:%s" %(net['name'].ljust(20), net['uuid'].ljust(38), net['description'].ljust(30), net['vim_id'])
475     else:
476         print content['error']['description']
477         if args.verbose:
478             print yaml.safe_dump(content, indent=4, default_flow_style=False)
479     return result
480
481 def scenario_delete(args):
482     #print "scenario-delete",args
483     if args.all:
484         tenant = "any"
485     else:
486         tenant = _get_tenant()
487     todelete = _get_item_uuid("scenarios", args.name, tenant=tenant)
488     if not args.force:
489         r = raw_input("Delete scenario %s (y/N)? " %(args.name))
490         if  not (len(r)>0  and r[0].lower()=="y"):
491             return 0
492     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s" %(mano_host, mano_port, tenant, todelete)
493     mano_response = requests.delete(URLrequest)
494     logger.debug("openmano response: %s", mano_response.text )
495     result = 0 if mano_response.status_code==200 else mano_response.status_code
496     content = mano_response.json()
497     #print json.dumps(content, indent=4)
498     if mano_response.status_code == 200:
499         print content['result']
500     else:
501         print content['error']['description']
502     return result
503
504 def scenario_deploy(args):
505     print "This command is deprecated, use 'openmano instance-scenario-create --scenario %s --name %s' instead!!!" % (args.scenario, args.name)
506     print
507     args.file = None
508     args.netmap_use = None
509     args.netmap_create = None
510     args.keypair = None
511     args.keypair_auto = None
512     return instance_create(args)
513
514 #     #print "scenario-deploy",args
515 #     headers_req = {'content-type': 'application/json'}
516 #     action = {}
517 #     actionCmd="start"
518 #     if args.nostart:
519 #         actionCmd="reserve"
520 #     action[actionCmd] = {}
521 #     action[actionCmd]["instance_name"] = args.name
522 #     if args.datacenter != None:
523 #         action[actionCmd]["datacenter"] = args.datacenter
524 #     elif mano_datacenter != None:
525 #         action[actionCmd]["datacenter"] = mano_datacenter
526 #         
527 #     if args.description:
528 #         action[actionCmd]["description"] = args.description
529 #     payload_req = json.dumps(action, indent=4)
530 #     #print payload_req
531
532 #     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, mano_tenant, args.scenario)
533 #     logger.debug("openmano request: %s", payload_req)
534 #     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
535 #     logger.debug("openmano response: %s", mano_response.text )
536 #     if args.verbose==None:
537 #         args.verbose=0
538 #     
539 #     result = 0 if mano_response.status_code==200 else mano_response.status_code
540 #     content = mano_response.json()
541 #     #print json.dumps(content, indent=4)
542 #     if args.verbose >= 3:
543 #         print yaml.safe_dump(content, indent=4, default_flow_style=False)
544 #         return result
545
546 #     if mano_response.status_code == 200:
547 #         myoutput = "%s %s" %(content['uuid'].ljust(38),content['name'].ljust(20))
548 #         if args.verbose >=1:
549 #             myoutput = "%s %s" %(myoutput, content['created_at'].ljust(20))
550 #         if args.verbose >=2:
551 #             myoutput = "%s %s %s" %(myoutput, content['description'].ljust(30))
552 #         print myoutput
553 #         print ""
554 #         print "To check the status, run the following command:"
555 #         print "openmano instance-scenario-list <instance_id>"
556 #     else:
557 #         print content['error']['description']
558 #     return result
559
560 def scenario_verify(args):
561     #print "scenario-verify",args
562     tenant = _get_tenant()
563     headers_req = {'content-type': 'application/json'}
564     action = {}
565     action["verify"] = {}
566     action["verify"]["instance_name"] = "scen-verify-return5"
567     payload_req = json.dumps(action, indent=4)
568     #print payload_req
569
570     URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, tenant, args.scenario)
571     logger.debug("openmano request: %s", payload_req)
572     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
573     logger.debug("openmano response: %s", mano_response.text )
574     
575     result = 0 if mano_response.status_code==200 else mano_response.status_code
576     content = mano_response.json()
577     #print json.dumps(content, indent=4)
578     if mano_response.status_code == 200:
579         print content['result']
580     else:
581         print content['error']['description']
582     return result
583
584 def instance_create(args):
585     tenant = _get_tenant()
586     headers_req = {'content-type': 'application/yaml'}
587     myInstance={"instance": {}, "schema_version": "0.1"}
588     if args.file:
589         instance_dict = _load_file_or_yaml(args.file)
590         if "instance" not in instance_dict:
591             myInstance = {"instance": instance_dict, "schema_version": "0.1"}
592         else:
593             myInstance = instance_dict
594     if args.name:
595         myInstance["instance"]['name'] = args.name
596     if args.description:
597         myInstance["instance"]['description'] = args.description
598     if args.nostart:
599         myInstance["instance"]['action'] = "reserve"
600     #datacenter
601     datacenter = myInstance["instance"].get("datacenter")
602     if args.datacenter != None:
603         datacenter = args.datacenter
604     myInstance["instance"]["datacenter"] = _get_datacenter(datacenter, tenant)
605     #scenario
606     scenario = myInstance["instance"].get("scenario")
607     if args.scenario != None:
608         scenario = args.scenario
609     if not scenario:
610         print "you must provide a scenario in the file descriptor or with --scenario"
611         return -1
612     myInstance["instance"]["scenario"] = _get_item_uuid("scenarios", scenario, tenant)
613     if args.netmap_use:
614         if "networks" not in myInstance["instance"]:
615             myInstance["instance"]["networks"] = {}
616         for net in args.netmap_use:
617             net_comma_list = net.split(",")
618             for net_comma in net_comma_list:
619                 net_tuple = net_comma.split("=")
620                 if len(net_tuple) != 2:
621                     print "error at netmap-use. Expected net-scenario=net-datacenter. (%s)?" % net_comma
622                     return
623                 net_scenario   = net_tuple[0].strip()
624                 net_datacenter = net_tuple[1].strip()
625                 if net_scenario not in myInstance["instance"]["networks"]:
626                     myInstance["instance"]["networks"][net_scenario] = {} 
627                 if "sites" not in myInstance["instance"]["networks"][net_scenario]:
628                     myInstance["instance"]["networks"][net_scenario]["sites"] = [ {} ]
629                 myInstance["instance"]["networks"][net_scenario]["sites"][0]["netmap-use"] = net_datacenter
630     if args.netmap_create:
631         if "networks" not in myInstance["instance"]:
632             myInstance["instance"]["networks"] = {}
633         for net in args.netmap_create:
634             net_comma_list = net.split(",")
635             for net_comma in net_comma_list:
636                 net_tuple = net_comma.split("=")
637                 if len(net_tuple) == 1:
638                     net_scenario   = net_tuple[0].strip()
639                     net_datacenter = None
640                 elif len(net_tuple) == 2:
641                     net_scenario   = net_tuple[0].strip()
642                     net_datacenter = net_tuple[1].strip()
643                 else:
644                     print "error at netmap-create. Expected net-scenario=net-datacenter or net-scenario. (%s)?" % net_comma
645                     return
646                 if net_scenario not in myInstance["instance"]["networks"]:
647                     myInstance["instance"]["networks"][net_scenario] = {} 
648                 if "sites" not in myInstance["instance"]["networks"][net_scenario]:
649                     myInstance["instance"]["networks"][net_scenario]["sites"] = [ {} ]
650                 myInstance["instance"]["networks"][net_scenario]["sites"][0]["netmap-create"] = net_datacenter
651     if args.keypair:
652         if "cloud-config" not in myInstance["instance"]:
653             myInstance["instance"]["cloud-config"] = {}
654         cloud_config = myInstance["instance"]["cloud-config"]
655         for key in args.keypair:
656             index = key.find(":")
657             if index<0:
658                 if "key-pairs" not in cloud_config:
659                     cloud_config["key-pairs"] = []
660                 cloud_config["key-pairs"].append(key)
661             else:
662                 user = key[:index]
663                 key_ = key[index+1:]
664                 key_list = key_.split(",")
665                 if "users" not in cloud_config:
666                     cloud_config["users"] = []
667                 cloud_config["users"].append({"name": user, "key-pairs": key_list  })
668     if args.keypair_auto:
669         try:
670             keys=[]
671             home = os.getenv("HOME")
672             user = os.getenv("USER")
673             files = os.listdir(home+'/.ssh')
674             for file in files:
675                 if file[-4:] == ".pub":
676                     with open(home+'/.ssh/'+file, 'r') as f:
677                         keys.append(f.read())
678             if not keys:
679                 print "Cannot obtain any public ssh key from '{}'. Try not using --keymap-auto".format(home+'/.ssh')
680                 return 1
681         except Exception as e:
682             print "Cannot obtain any public ssh key. Error '{}'. Try not using --keymap-auto".format(str(e))
683             return 1
684         
685         if "cloud-config" not in myInstance["instance"]:
686             myInstance["instance"]["cloud-config"] = {}
687         cloud_config = myInstance["instance"]["cloud-config"]
688         if "key-pairs" not in cloud_config:
689             cloud_config["key-pairs"] = []
690         if user:
691             if "users" not in cloud_config:
692                 cloud_config["users"] = []
693             cloud_config["users"].append({"name": user, "key-pairs": keys })                    
694                         
695     payload_req = yaml.safe_dump(myInstance, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
696     logger.debug("openmano request: %s", payload_req)
697     URLrequest = "http://%s:%s/openmano/%s/instances" %(mano_host, mano_port, tenant)
698     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
699     logger.debug("openmano response: %s", mano_response.text )
700     if args.verbose==None:
701         args.verbose=0
702     
703     result = 0 if mano_response.status_code==200 else mano_response.status_code
704     content = mano_response.json()
705     #print json.dumps(content, indent=4)
706     if args.verbose >= 3:
707         print yaml.safe_dump(content, indent=4, default_flow_style=False)
708         return result
709
710     if mano_response.status_code == 200:
711         myoutput = "%s %s" %(content['uuid'].ljust(38),content['name'].ljust(20))
712         if args.verbose >=1:
713             myoutput = "%s %s" %(myoutput, content['created_at'].ljust(20))
714         if args.verbose >=2:
715             myoutput = "%s %s %s" %(myoutput, content['description'].ljust(30))
716         print myoutput
717     else:
718         print content['error']['description']
719     return result
720
721 def instance_scenario_list(args):
722     #print "instance-scenario-list",args
723     if args.all:
724         tenant = "any"
725     else:
726         tenant = _get_tenant()
727     if args.name:
728         toshow = _get_item_uuid("instances", args.name, tenant)
729         URLrequest = "http://%s:%s/openmano/%s/instances/%s" %(mano_host, mano_port, tenant, toshow)
730     else:
731         URLrequest = "http://%s:%s/openmano/%s/instances" %(mano_host, mano_port, tenant)
732     mano_response = requests.get(URLrequest)
733     logger.debug("openmano response: %s", mano_response.text )
734     content = mano_response.json()
735     #print json.dumps(content, indent=4)
736     if args.verbose==None:
737         args.verbose=0
738
739     result = 0 if mano_response.status_code==200 else mano_response.status_code
740     if mano_response.status_code == 200:
741         if not args.name:
742             if args.verbose >= 3:
743                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
744                 return result
745             if len(content['instances']) == 0:
746                 print "No scenario instances were found."
747                 return result
748             for instance in content['instances']:
749                 myoutput = "%s %s" %(instance['uuid'].ljust(38),instance['name'].ljust(20))
750                 if args.verbose >=1:
751                     myoutput = "%s %s" %(myoutput, instance['created_at'].ljust(20))
752                 print myoutput
753                 if args.verbose >=2:
754                     print "Description: %s" %instance['description']
755         else:
756             if args.verbose:
757                 print yaml.safe_dump(content, indent=4, default_flow_style=False)
758                 return result
759             instance = content
760             print "%s %s %s" %(instance['uuid'].ljust(38),instance['name'].ljust(20),instance['created_at'].ljust(20))
761             print "Description: %s" %instance['description']
762             print "Template scenario id: %s" %instance['scenario_id']
763             print "Template scenario name: %s" %instance['scenario_name']
764             print "---------------------------------------"
765             print "VNF instances: %d" %len(instance['vnfs'])
766             for vnf in instance['vnfs']:
767                 #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))
768                 print "    %s %s Template vnf id: %s" %(vnf['uuid'].ljust(38), vnf['vnf_name'].ljust(20), vnf['vnf_id'].ljust(38))
769             if len(instance['nets'])>0:
770                 print "---------------------------------------"
771                 print "Internal nets:"
772                 for net in instance['nets']:
773                     if net['created']:
774                         print "    %s %s VIM ID: %s" %(net['uuid'].ljust(38), net['status'].ljust(12), net['vim_net_id'])
775                 print "---------------------------------------"
776                 print "External nets:"
777                 for net in instance['nets']:
778                     if not net['created']:
779                         print "    %s %s VIM ID: %s" %(net['uuid'].ljust(38), net['status'].ljust(12), net['vim_net_id'])
780             print "---------------------------------------"
781             print "VM instances:"
782             for vnf in instance['vnfs']:
783                 for vm in vnf['vms']:
784                     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'])
785     else:
786         print content['error']['description']
787         if args.verbose:
788             print yaml.safe_dump(content, indent=4, default_flow_style=False)
789     return result
790
791 def instance_scenario_status(args):
792     print "instance-scenario-status"
793     return 0
794
795 def instance_scenario_delete(args):
796     if args.all:
797         tenant = "any"
798     else:
799         tenant = _get_tenant()
800     todelete = _get_item_uuid("instances", args.name, tenant=tenant)
801     #print "instance-scenario-delete",args
802     if not args.force:
803         r = raw_input("Delete scenario instance %s (y/N)? " %(args.name))
804         if  not (len(r)>0  and r[0].lower()=="y"):
805             return
806     URLrequest = "http://%s:%s/openmano/%s/instances/%s" %(mano_host, mano_port, tenant, todelete)
807     mano_response = requests.delete(URLrequest)
808     logger.debug("openmano response: %s", mano_response.text )
809     result = 0 if mano_response.status_code==200 else mano_response.status_code
810     content = mano_response.json()
811     #print json.dumps(content, indent=4)
812     if mano_response.status_code == 200:
813         print content['result']
814     else:
815         print content['error']['description']
816     return result
817
818 def instance_scenario_action(args):
819     #print "instance-scenario-action", args
820     tenant = _get_tenant()
821     toact = _get_item_uuid("instances", args.name, tenant=tenant)
822     action={}
823     action[ args.action ] = args.param
824     if args.vnf:
825         action["vnfs"] = args.vnf
826     if args.vm:
827         action["vms"] = args.vm
828     
829     headers_req = {'content-type': 'application/json'}
830     payload_req = json.dumps(action, indent=4)
831     URLrequest = "http://%s:%s/openmano/%s/instances/%s/action" %(mano_host, mano_port, tenant, toact)
832     logger.debug("openmano request: %s", payload_req)
833     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
834     logger.debug("openmano response: %s", mano_response.text )
835     result = 0 if mano_response.status_code==200 else mano_response.status_code
836     content = mano_response.json()
837     #print json.dumps(content, indent=4)
838     if mano_response.status_code == 200:
839         if args.verbose:
840             print yaml.safe_dump(content, indent=4, default_flow_style=False)
841             return result
842         for uuid,c in content.iteritems():
843             print "%s %s %s" %(uuid.ljust(38), c['name'].ljust(20),c['description'].ljust(20))
844     else:
845         print content['error']['description']
846     return result
847
848
849 def instance_vnf_list(args):
850     print "instance-vnf-list"
851     return 0
852
853 def instance_vnf_status(args):
854     print "instance-vnf-status"
855     return 0
856
857 def tenant_create(args):
858     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
859     tenant_dict={"name": args.name}
860     if args.description!=None:
861         tenant_dict["description"] = args.description 
862     payload_req = json.dumps( {"tenant": tenant_dict })
863     
864     #print payload_req
865         
866     URLrequest = "http://%s:%s/openmano/tenants" %(mano_host, mano_port)
867     logger.debug("openmano request: %s", payload_req)
868     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
869     logger.debug("openmano response: %s", mano_response.text )
870     return _print_verbose(mano_response, args.verbose)
871
872 def tenant_list(args):
873     #print "tenant-list",args
874     if args.name:
875         toshow = _get_item_uuid("tenants", args.name)
876         URLrequest = "http://%s:%s/openmano/tenants/%s" %(mano_host, mano_port, toshow)
877     else:
878         URLrequest = "http://%s:%s/openmano/tenants" %(mano_host, mano_port)
879     mano_response = requests.get(URLrequest)
880     logger.debug("openmano response: %s", mano_response.text )
881     if args.verbose==None:
882         args.verbose=0
883     if args.name!=None:
884         args.verbose += 1
885     return _print_verbose(mano_response, args.verbose)
886
887 def tenant_delete(args):
888     #print "tenant-delete",args
889     todelete = _get_item_uuid("tenants", args.name)
890     if not args.force:
891         r = raw_input("Delete tenant %s (y/N)? " %(args.name))
892         if  not (len(r)>0  and r[0].lower()=="y"):
893             return 0
894     URLrequest = "http://%s:%s/openmano/tenants/%s" %(mano_host, mano_port, todelete)
895     mano_response = requests.delete(URLrequest)
896     logger.debug("openmano response: %s", mano_response.text )
897     result = 0 if mano_response.status_code==200 else mano_response.status_code
898     content = mano_response.json()
899     #print json.dumps(content, indent=4)
900     if mano_response.status_code == 200:
901         print content['result']
902     else:
903         print content['error']['description']
904     return result
905
906 def datacenter_attach(args):
907     tenant = _get_tenant()
908     datacenter = _get_datacenter(args.name)
909     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
910     
911     datacenter_dict={}
912     if args.vim_tenant_id != None:
913         datacenter_dict['vim_tenant'] = args.vim_tenant_id
914     if args.vim_tenant_name != None:
915         datacenter_dict['vim_tenant_name'] = args.vim_tenant_name
916     if args.user != None:
917         datacenter_dict['vim_username'] = args.user
918     if args.password != None:
919         datacenter_dict['vim_password'] = args.password
920     if args.config!=None:
921         datacenter_dict["config"] = _load_file_or_yaml(args.config)
922     payload_req = json.dumps( {"datacenter": datacenter_dict })
923     
924     #print payload_req
925         
926     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, datacenter)
927     logger.debug("openmano request: %s", payload_req)
928     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
929     logger.debug("openmano response: %s", mano_response.text )
930     result = _print_verbose(mano_response, args.verbose)
931     #provide addional information if error
932     if mano_response.status_code != 200:
933         content = mano_response.json()
934         if "already in use for  'name'" in content['error']['description'] and \
935                 "to database vim_tenants table" in content['error']['description']:
936             print "Try to specify a different name with --vim-tenant-name"
937     return result
938
939
940 def datacenter_edit_vim_tenant(args):
941     tenant = _get_tenant()
942     datacenter = _get_datacenter(args.name)
943     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
944
945     if not (args.vim_tenant_id or args.vim_tenant_name or args.user or args.password or args.config):
946         raise OpenmanoCLIError("Error. At least one parameter must be updated.")
947
948     datacenter_dict = {}
949     if args.vim_tenant_id != None:
950         datacenter_dict['vim_tenant'] = args.vim_tenant_id
951     if args.vim_tenant_name != None:
952         datacenter_dict['vim_tenant_name'] = args.vim_tenant_name
953     if args.user != None:
954         datacenter_dict['vim_username'] = args.user
955     if args.password != None:
956         datacenter_dict['vim_password'] = args.password
957     if args.config != None:
958         datacenter_dict["config"] = _load_file_or_yaml(args.config)
959     payload_req = json.dumps({"datacenter": datacenter_dict})
960
961     # print payload_req
962
963     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" % (mano_host, mano_port, tenant, datacenter)
964     logger.debug("openmano request: %s", payload_req)
965     mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
966     logger.debug("openmano response: %s", mano_response.text)
967     result = _print_verbose(mano_response, args.verbose)
968
969     return result
970
971 def datacenter_detach(args):
972     if args.all:
973         tenant = "any"
974     else:
975         tenant = _get_tenant()
976     datacenter = _get_datacenter(args.name, tenant)
977     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
978     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, datacenter)
979     mano_response = requests.delete(URLrequest, headers=headers_req)
980     logger.debug("openmano response: %s", mano_response.text )
981     content = mano_response.json()
982     #print json.dumps(content, indent=4)
983     result = 0 if mano_response.status_code==200 else mano_response.status_code
984     if mano_response.status_code == 200:
985         print content['result']
986     else:
987         print content['error']['description']
988     return result
989
990 def datacenter_create(args):
991     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
992     datacenter_dict={"name": args.name, "vim_url": args.url}
993     if args.description!=None:
994         datacenter_dict["description"] = args.description 
995     if args.type!=None:
996         datacenter_dict["type"] = args.type 
997     if args.url!=None:
998         datacenter_dict["vim_url_admin"] = args.url_admin 
999     if args.config!=None:
1000         datacenter_dict["config"] = _load_file_or_yaml(args.config)
1001     if args.sdn_controller!=None:
1002         tenant = _get_tenant()
1003         sdn_controller = _get_item_uuid("sdn_controllers", args.sdn_controller, tenant)
1004         if not 'config' in datacenter_dict:
1005             datacenter_dict['config'] = {}
1006         datacenter_dict['config']['sdn-controller'] = sdn_controller
1007     payload_req = json.dumps( {"datacenter": datacenter_dict })
1008     
1009     #print payload_req
1010         
1011     URLrequest = "http://%s:%s/openmano/datacenters" %(mano_host, mano_port)
1012     logger.debug("openmano request: %s", payload_req)
1013     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
1014     logger.debug("openmano response: %s", mano_response.text )
1015     return _print_verbose(mano_response, args.verbose)
1016
1017 def datacenter_delete(args):
1018     #print "datacenter-delete",args
1019     todelete = _get_item_uuid("datacenters", args.name, "any")
1020     if not args.force:
1021         r = raw_input("Delete datacenter %s (y/N)? " %(args.name))
1022         if  not (len(r)>0  and r[0].lower()=="y"):
1023             return 0
1024     URLrequest = "http://%s:%s/openmano/datacenters/%s" %(mano_host, mano_port, todelete)
1025     mano_response = requests.delete(URLrequest)
1026     logger.debug("openmano response: %s", mano_response.text )
1027     result = 0 if mano_response.status_code==200 else mano_response.status_code
1028     content = mano_response.json()
1029     #print json.dumps(content, indent=4)
1030     if mano_response.status_code == 200:
1031         print content['result']
1032     else:
1033         print content['error']['description']
1034     return result
1035
1036
1037 def datacenter_list(args):
1038     #print "datacenter-list",args
1039     tenant='any' if args.all else _get_tenant()
1040     
1041     if args.name:
1042         toshow = _get_item_uuid("datacenters", args.name, tenant) 
1043         URLrequest = "http://%s:%s/openmano/%s/datacenters/%s" %(mano_host, mano_port, tenant, toshow)
1044     else:
1045         URLrequest = "http://%s:%s/openmano/%s/datacenters" %(mano_host, mano_port, tenant)
1046     mano_response = requests.get(URLrequest)
1047     logger.debug("openmano response: %s", mano_response.text )
1048     if args.verbose==None:
1049         args.verbose=0
1050     if args.name!=None:
1051         args.verbose += 1
1052     return _print_verbose(mano_response, args.verbose)
1053
1054
1055 def datacenter_sdn_port_mapping_set(args):
1056     tenant = _get_tenant()
1057     datacenter = _get_datacenter(args.name, tenant)
1058     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1059
1060     if not args.file:
1061         raise OpenmanoCLIError(
1062             "No yaml/json has been provided specifying the SDN port mapping")
1063     sdn_port_mapping = _load_file_or_yaml(args.file)
1064     payload_req = json.dumps({"sdn_port_mapping": sdn_port_mapping})
1065
1066     # read
1067     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter)
1068     mano_response = requests.get(URLrequest)
1069     logger.debug("openmano response: %s", mano_response.text)
1070     port_mapping = mano_response.json()
1071     if mano_response.status_code != 200:
1072         str(mano_response.json())
1073         raise OpenmanoCLIError("openmano client error: {}".format(port_mapping['error']['description']))
1074     if len(port_mapping["sdn_port_mapping"]["ports_mapping"]) > 0:
1075         if not args.force:
1076             r = raw_input("Datacenter %s already contains a port mapping. Overwrite? (y/N)? " % (datacenter))
1077             if not (len(r) > 0 and r[0].lower() == "y"):
1078                 return 0
1079
1080         # clear
1081         URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter)
1082         mano_response = requests.delete(URLrequest)
1083         logger.debug("openmano response: %s", mano_response.text)
1084         if mano_response.status_code != 200:
1085             return _print_verbose(mano_response, args.verbose)
1086
1087     # set
1088     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter)
1089     logger.debug("openmano request: %s", payload_req)
1090     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
1091     logger.debug("openmano response: %s", mano_response.text)
1092     return _print_verbose(mano_response, args.verbose)
1093
1094
1095 def datacenter_sdn_port_mapping_list(args):
1096     tenant = _get_tenant()
1097     datacenter = _get_datacenter(args.name, tenant)
1098
1099     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter)
1100     mano_response = requests.get(URLrequest)
1101     logger.debug("openmano response: %s", mano_response.text)
1102
1103     return _print_verbose(mano_response, 4)
1104
1105
1106 def datacenter_sdn_port_mapping_clear(args):
1107     tenant = _get_tenant()
1108     datacenter = _get_datacenter(args.name, tenant)
1109
1110     if not args.force:
1111         r = raw_input("Clean SDN port mapping for datacenter %s (y/N)? " %(datacenter))
1112         if not (len(r) > 0 and r[0].lower() == "y"):
1113             return 0
1114
1115     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter)
1116     mano_response = requests.delete(URLrequest)
1117     logger.debug("openmano response: %s", mano_response.text)
1118
1119     return _print_verbose(mano_response, args.verbose)
1120
1121
1122 def sdn_controller_create(args):
1123     tenant = _get_tenant()
1124     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1125
1126     error_msg=[]
1127     if not args.ip: error_msg.append("'ip'")
1128     if not args.port: error_msg.append("'port'")
1129     if not args.dpid: error_msg.append("'dpid'")
1130     if not args.type: error_msg.append("'type'")
1131     if error_msg:
1132         raise OpenmanoCLIError("The following arguments are required: " + ",".join(error_msg))
1133
1134     controller_dict = {}
1135     controller_dict['name'] = args.name
1136     controller_dict['ip'] = args.ip
1137     controller_dict['port'] = int(args.port)
1138     controller_dict['dpid'] = args.dpid
1139     controller_dict['type'] = args.type
1140     if args.description != None:
1141         controller_dict['description'] = args.description
1142     if args.user != None:
1143         controller_dict['user'] = args.user
1144     if args.password != None:
1145         controller_dict['password'] = args.password
1146
1147     payload_req = json.dumps({"sdn_controller": controller_dict})
1148
1149     # print payload_req
1150
1151     URLrequest = "http://%s:%s/openmano/%s/sdn_controllers" % (mano_host, mano_port, tenant)
1152     logger.debug("openmano request: %s", payload_req)
1153     mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
1154     logger.debug("openmano response: %s", mano_response.text)
1155     result = _print_verbose(mano_response, args.verbose)
1156     return result
1157
1158
1159 def sdn_controller_edit(args):
1160     tenant = _get_tenant()
1161     controller_uuid = _get_item_uuid("sdn_controllers", args.name, tenant)
1162     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1163
1164     controller_dict = {}
1165     if args.new_name:
1166         controller_dict['name'] = args.new_name
1167     if args.ip:
1168         controller_dict['ip'] = args.ip
1169     if args.port:
1170         controller_dict['port'] = int(args.port)
1171     if args.dpid:
1172         controller_dict['dpid'] = args.dpid
1173     if args.type:
1174         controller_dict['type'] = args.type
1175     if args.description:
1176         controller_dict['description'] = args.description
1177     if args.user:
1178         controller_dict['user'] = args.user
1179     if args.password:
1180         controller_dict['password'] = args.password
1181
1182     if not controller_dict:
1183         raise OpenmanoCLIError("At least one parameter must be edited")
1184
1185     if not args.force:
1186         r = raw_input("Update SDN controller {} (y/N)? ".format(args.name))
1187         if not (len(r) > 0 and r[0].lower() == "y"):
1188             return 0
1189
1190     payload_req = json.dumps({"sdn_controller": controller_dict})
1191     # print payload_req
1192
1193     URLrequest = "http://%s:%s/openmano/%s/sdn_controllers/%s" % (mano_host, mano_port, tenant, controller_uuid)
1194     logger.debug("openmano request: %s", payload_req)
1195     mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1196     logger.debug("openmano response: %s", mano_response.text)
1197     result = _print_verbose(mano_response, args.verbose)
1198     return result
1199
1200
1201 def sdn_controller_list(args):
1202     tenant = _get_tenant()
1203     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1204
1205     if args.name:
1206         toshow = _get_item_uuid("sdn_controllers", args.name, tenant)
1207         URLrequest = "http://%s:%s/openmano/%s/sdn_controllers/%s" %(mano_host, mano_port, tenant, toshow)
1208     else:
1209         URLrequest = "http://%s:%s/openmano/%s/sdn_controllers" %(mano_host, mano_port, tenant)
1210     #print URLrequest
1211     mano_response = requests.get(URLrequest)
1212     logger.debug("openmano response: %s", mano_response.text )
1213     if args.verbose==None:
1214         args.verbose=0
1215     if args.name!=None:
1216         args.verbose += 1
1217
1218     # json.dumps(mano_response.json(), indent=4)
1219     return _print_verbose(mano_response, args.verbose)
1220
1221
1222 def sdn_controller_delete(args):
1223     tenant = _get_tenant()
1224     controller_uuid = _get_item_uuid("sdn_controllers", args.name, tenant)
1225
1226     if not args.force:
1227         r = raw_input("Delete SDN controller %s (y/N)? " % (args.name))
1228         if not (len(r) > 0 and r[0].lower() == "y"):
1229             return 0
1230
1231     URLrequest = "http://%s:%s/openmano/%s/sdn_controllers/%s" % (mano_host, mano_port, tenant, controller_uuid)
1232     mano_response = requests.delete(URLrequest)
1233     logger.debug("openmano response: %s", mano_response.text)
1234     return _print_verbose(mano_response, args.verbose)
1235
1236 def vim_action(args):
1237     #print "datacenter-net-action",args
1238     tenant = _get_tenant()
1239     datacenter = _get_datacenter(args.datacenter, tenant)
1240     if args.verbose==None:
1241         args.verbose=0
1242     if args.action=="list":
1243         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, tenant, datacenter, args.item)
1244         if args.name!=None:
1245             args.verbose += 1
1246             URLrequest += "/" + args.name
1247         mano_response = requests.get(URLrequest)
1248         logger.debug("openmano response: %s", mano_response.text )
1249         return _print_verbose(mano_response, args.verbose)
1250     elif args.action=="delete":
1251         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss/%s" %(mano_host, mano_port, tenant, datacenter, args.item, args.name)
1252         mano_response = requests.delete(URLrequest)
1253         logger.debug("openmano response: %s", mano_response.text )
1254         result = 0 if mano_response.status_code==200 else mano_response.status_code
1255         content = mano_response.json()
1256         #print json.dumps(content, indent=4)
1257         if mano_response.status_code == 200:
1258             print content['result']
1259         else:
1260             print content['error']['description']
1261         return result
1262     elif args.action=="create":
1263         headers_req = {'content-type': 'application/yaml'}
1264         if args.file:
1265             create_dict = _load_file_or_yaml(args.file)
1266             if args.item not in create_dict:
1267                 create_dict = {args.item: create_dict}
1268         else:
1269             create_dict = {args.item:{}}
1270         if args.name:
1271             create_dict[args.item]['name'] = args.name
1272         #if args.description:
1273         #    create_dict[args.item]['description'] = args.description
1274         if args.item=="network":
1275             if args.bind_net:
1276                 create_dict[args.item]['bind_net'] = args.bind_net
1277             if args.type:
1278                 create_dict[args.item]['type'] = args.type
1279             if args.shared:
1280                 create_dict[args.item]['shared'] = args.shared
1281         if "name" not in create_dict[args.item]:
1282             print "You must provide a name in the descriptor file or with the --name option"
1283             return
1284         payload_req = yaml.safe_dump(create_dict, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
1285         logger.debug("openmano request: %s", payload_req)
1286         URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, tenant, datacenter, args.item)
1287         mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
1288         logger.debug("openmano response: %s", mano_response.text )
1289         if args.verbose==None:
1290             args.verbose=0
1291         return _print_verbose(mano_response, args.verbose)
1292
1293
1294 def _get_items(item, item_name_id=None, datacenter=None, tenant=None):
1295     URLrequest = "http://%s:%s/openmano" %(mano_host, mano_port)
1296     if tenant:
1297         URLrequest += "/" + tenant
1298     if datacenter:
1299         URLrequest += "/vim/" + datacenter
1300     if item:
1301         URLrequest += "/" + item +"s"
1302     if item_name_id:
1303         URLrequest += "/" + item_name_id
1304     mano_response = requests.get(URLrequest)
1305     logger.debug("openmano response: %s", mano_response.text )
1306
1307     return mano_response
1308
1309
1310 def vim_net_sdn_attach(args):
1311     #Verify the network exists in the vim
1312     tenant = _get_tenant()
1313     datacenter = _get_datacenter(args.datacenter, tenant)
1314     result = _get_items('network', item_name_id=args.vim_net, datacenter=datacenter, tenant=tenant)
1315     content = yaml.load(result.content)
1316     if 'networks' in content:
1317         raise OpenmanoCLIError('More than one network in the vim named ' + args.vim_net + '. Use uuid instead')
1318     if 'error' in content:
1319         raise OpenmanoCLIError(yaml.safe_dump(content))
1320     network_uuid = content['network']['id']
1321
1322     #Make call to attach the dataplane port to the SND network associated to the vim network
1323     headers_req = {'content-type': 'application/yaml'}
1324     payload_req = {'port': args.port}
1325     if args.vlan:
1326         payload_req['vlan'] = int(args.vlan)
1327     if args.mac:
1328         payload_req['mac'] = args.mac
1329
1330     URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/attach" % (mano_host, mano_port, tenant, datacenter, network_uuid)
1331     logger.debug("openmano request: %s", payload_req)
1332     mano_response = requests.post(URLrequest, headers=headers_req, data=json.dumps(payload_req))
1333     logger.debug("openmano response: %s", mano_response.text)
1334     result = _print_verbose(mano_response, args.verbose)
1335     return result
1336
1337
1338 def vim_net_sdn_detach(args):
1339     if not args.all and not args.id:
1340         print "--all or --id must be used"
1341         return 1
1342
1343     # Verify the network exists in the vim
1344     tenant = _get_tenant()
1345     datacenter = _get_datacenter(args.datacenter, tenant)
1346     result = _get_items('network', item_name_id=args.vim_net, datacenter=datacenter, tenant=tenant)
1347     content = yaml.load(result.content)
1348     if 'networks' in content:
1349         raise OpenmanoCLIError('More than one network in the vim named ' + args.vim_net + '. Use uuid instead')
1350     if 'error' in content:
1351         raise OpenmanoCLIError(yaml.safe_dump(content))
1352     network_uuid = content['network']['id']
1353
1354     if not args.force:
1355         r = raw_input("Confirm action' (y/N)? ")
1356         if len(r) == 0 or r[0].lower() != "y":
1357             return 0
1358
1359     if args.id:
1360         URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/detach/%s" % (
1361             mano_host, mano_port, tenant, datacenter, network_uuid, args.id)
1362     else:
1363         URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/detach" % (
1364             mano_host, mano_port, tenant, datacenter, network_uuid)
1365     mano_response = requests.delete(URLrequest)
1366     logger.debug("openmano response: %s", mano_response.text)
1367     result = _print_verbose(mano_response, args.verbose)
1368     return result
1369
1370
1371 def datacenter_net_action(args):
1372     if args.action == "net-update":
1373         print "This command is deprecated, use 'openmano datacenter-netmap-delete --all' and 'openmano datacenter-netmap-import' instead!!!"
1374         print
1375         args.action = "netmap-delete"
1376         args.netmap = None
1377         args.all = True
1378         r = datacenter_netmap_action(args)
1379         if r == 0:
1380             args.force = True
1381             args.action = "netmap-import"
1382             r = datacenter_netmap_action(args)
1383         return r
1384
1385     if args.action == "net-edit":
1386         args.netmap = args.net
1387         args.name = None
1388     elif args.action == "net-list":
1389         args.netmap = None
1390     elif args.action == "net-delete":
1391         args.netmap = args.net
1392         args.all = False
1393           
1394     args.action = "netmap" + args.action[3:]
1395     args.vim_name=None
1396     args.vim_id=None
1397     print "This command is deprecated, use 'openmano datacenter-%s' instead!!!" % args.action
1398     print
1399     return datacenter_netmap_action(args)
1400
1401 def datacenter_netmap_action(args):
1402     tenant = _get_tenant()
1403     datacenter = _get_datacenter(args.datacenter, tenant)
1404     #print "datacenter_netmap_action",args
1405     payload_req = None
1406     if args.verbose==None:
1407         args.verbose=0
1408     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1409     URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/netmaps" %(mano_host, mano_port, tenant, datacenter)
1410         
1411     if args.action=="netmap-list":
1412         if args.netmap:
1413             URLrequest += "/" + args.netmap
1414             args.verbose += 1
1415         mano_response = requests.get(URLrequest)
1416             
1417     elif args.action=="netmap-delete":
1418         if args.netmap and args.all:
1419             print "you can not use a netmap name and the option --all at the same time"
1420             return 1
1421         if args.netmap:
1422             force_text= "Delete default netmap '%s' from datacenter '%s' (y/N)? " % (args.netmap, datacenter)
1423             URLrequest += "/" + args.netmap
1424         elif args.all: 
1425             force_text="Delete all default netmaps from datacenter '%s' (y/N)? " % (datacenter)
1426         else:
1427             print "you must specify a netmap name or the option --all"
1428             return 1
1429         if not args.force:
1430             r = raw_input(force_text)
1431             if  len(r)>0  and r[0].lower()=="y":
1432                 pass
1433             else:
1434                 return 0
1435         mano_response = requests.delete(URLrequest, headers=headers_req)
1436     elif args.action=="netmap-import":
1437         if not args.force:
1438             r = raw_input("Create all the available networks from datacenter '%s' as default netmaps (y/N)? " % (datacenter))
1439             if  len(r)>0  and r[0].lower()=="y":
1440                 pass
1441             else:
1442                 return 0
1443         URLrequest += "/upload"
1444         mano_response = requests.post(URLrequest, headers=headers_req)
1445     elif args.action=="netmap-edit" or args.action=="netmap-create":
1446         if args.file:
1447             payload = _load_file_or_yaml(args.file)
1448         else:
1449             payload = {}
1450         if "netmap" not in payload:
1451             payload = {"netmap": payload}
1452         if args.name:
1453             payload["netmap"]["name"] = args.name
1454         if args.vim_id:
1455             payload["netmap"]["vim_id"] = args.vim_id
1456         if args.action=="netmap-create" and args.vim_name:
1457             payload["netmap"]["vim_name"] = args.vim_name
1458         payload_req = json.dumps(payload)
1459         logger.debug("openmano request: %s", payload_req)
1460         
1461         if args.action=="netmap-edit" and not args.force:
1462             if len(payload["netmap"]) == 0:
1463                 print "You must supply some parameter to edit"
1464                 return 1
1465             r = raw_input("Edit default netmap '%s' from datacenter '%s' (y/N)? " % (args.netmap, datacenter))
1466             if  len(r)>0  and r[0].lower()=="y":
1467                 pass
1468             else:
1469                 return 0
1470             URLrequest += "/" + args.netmap
1471             mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1472         else: #netmap-create
1473             if "vim_name" not in payload["netmap"] and "vim_id" not in payload["netmap"]:
1474                 print "You must supply either --vim-id or --vim-name option; or include one of them in the file descriptor"
1475                 return 1
1476             mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req)
1477
1478     logger.debug("openmano response: %s", mano_response.text )
1479     return _print_verbose(mano_response, args.verbose)
1480
1481
1482 def element_edit(args):
1483     element = _get_item_uuid(args.element, args.name)
1484     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1485     URLrequest = "http://%s:%s/openmano/%s/%s" %(mano_host, mano_port, args.element, element)
1486     payload=_load_file_or_yaml(args.file)
1487     if args.element[:-1] not in payload:
1488         payload = {args.element[:-1]: payload }
1489     payload_req = json.dumps(payload)
1490     
1491     #print payload_req
1492     if not args.force or (args.name==None and args.filer==None):
1493         r = raw_input(" Edit " + args.element[:-1] + " " + args.name + " (y/N)? ")
1494         if  len(r)>0  and r[0].lower()=="y":
1495             pass
1496         else:
1497             return 0
1498     logger.debug("openmano request: %s", payload_req)
1499     mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1500     logger.debug("openmano response: %s", mano_response.text )
1501     if args.verbose==None:
1502         args.verbose=0
1503     if args.name!=None:
1504         args.verbose += 1
1505     return _print_verbose(mano_response, args.verbose)
1506
1507
1508 def datacenter_edit(args):
1509     tenant = _get_tenant()
1510     element = _get_item_uuid('datacenters', args.name, tenant)
1511     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1512     URLrequest = "http://%s:%s/openmano/datacenters/%s" % (mano_host, mano_port, element)
1513
1514     has_arguments = False
1515     if args.file != None:
1516         has_arguments = True
1517         payload = _load_file_or_yaml(args.file)
1518     else:
1519         payload = {}
1520
1521     if args.sdn_controller != None:
1522         has_arguments = True
1523         if not 'config' in payload:
1524             payload['config'] = {}
1525         if not 'sdn-controller' in payload['config']:
1526             payload['config']['sdn-controller'] = {}
1527         if args.sdn_controller == 'null':
1528             payload['config']['sdn-controller'] = None
1529         else:
1530             payload['config']['sdn-controller'] = _get_item_uuid("sdn_controllers", args.sdn_controller, tenant)
1531
1532     if not has_arguments:
1533         raise OpenmanoCLIError("At least one argument must be provided to modify the datacenter")
1534
1535     if 'datacenter' not in payload:
1536         payload = {'datacenter': payload}
1537     payload_req = json.dumps(payload)
1538
1539     # print payload_req
1540     if not args.force or (args.name == None and args.filer == None):
1541         r = raw_input(" Edit datacenter " + args.name + " (y/N)? ")
1542         if len(r) > 0 and r[0].lower() == "y":
1543             pass
1544         else:
1545             return 0
1546     logger.debug("openmano request: %s", payload_req)
1547     mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req)
1548     logger.debug("openmano response: %s", mano_response.text)
1549     if args.verbose == None:
1550         args.verbose = 0
1551     if args.name != None:
1552         args.verbose += 1
1553     return _print_verbose(mano_response, args.verbose)
1554
1555
1556 def version(args):
1557     headers_req = {'Accept': 'application/json', 'content-type': 'application/json'}
1558     URLrequest = "http://%s:%s/openmano/version" % (mano_host, mano_port)
1559
1560     mano_response = requests.get(URLrequest, headers=headers_req)
1561     logger.debug("openmano response: %s", mano_response.text)
1562     print mano_response.text
1563
1564
1565 global mano_host
1566 global mano_port
1567 global mano_tenant
1568
1569 if __name__=="__main__":
1570     
1571     mano_tenant = os.getenv('OPENMANO_TENANT', None)
1572     mano_host = os.getenv('OPENMANO_HOST',"localhost")
1573     mano_port = os.getenv('OPENMANO_PORT',"9090")
1574     mano_datacenter = os.getenv('OPENMANO_DATACENTER',None)
1575     
1576     main_parser = ThrowingArgumentParser(description='User program to interact with OPENMANO-SERVER (openmanod)')
1577     main_parser.add_argument('--version', action='version', help="get version of this client",
1578                             version='%(prog)s client version ' + __version__ +
1579                                     " (Note: use '%(prog)s version' to get server version)")
1580
1581     subparsers = main_parser.add_subparsers(help='commands')
1582     
1583     parent_parser = argparse.ArgumentParser(add_help=False)
1584     parent_parser.add_argument('--verbose', '-v', action='count', help="increase verbosity level. Use several times")
1585     parent_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
1586
1587     config_parser = subparsers.add_parser('config', parents=[parent_parser], help="prints configuration values")
1588     config_parser.add_argument("-n", action="store_true", help="resolves tenant and datacenter names")
1589     config_parser.set_defaults(func=config)
1590
1591     version_parser = subparsers.add_parser('version', parents=[parent_parser], help="get server version")
1592     version_parser.set_defaults(func=version)
1593
1594     vnf_create_parser = subparsers.add_parser('vnf-create', parents=[parent_parser], help="adds a vnf into the catalogue")
1595     vnf_create_parser.add_argument("file", action="store", help="location of the JSON file describing the VNF").completer = FilesCompleter
1596     vnf_create_parser.add_argument("--name", action="store", help="name of the VNF (if it exists in the VNF descriptor, it is overwritten)")
1597     vnf_create_parser.add_argument("--description", action="store", help="description of the VNF (if it exists in the VNF descriptor, it is overwritten)")
1598     vnf_create_parser.add_argument("--image-path", action="store",  help="change image path locations (overwritten)")
1599     vnf_create_parser.add_argument("--image-name", action="store",  help="change image name (overwritten)")
1600     vnf_create_parser.add_argument("--image-checksum", action="store",  help="change image checksum (overwritten)")
1601     vnf_create_parser.set_defaults(func=vnf_create)
1602
1603     vnf_list_parser = subparsers.add_parser('vnf-list', parents=[parent_parser], help="lists information about a vnf")
1604     vnf_list_parser.add_argument("name", nargs='?', help="name of the VNF")
1605     vnf_list_parser.add_argument("-a", "--all", action="store_true", help="shows all vnfs, not only the owned or public ones")
1606     #vnf_list_parser.add_argument('--descriptor', help="prints the VNF descriptor", action="store_true")
1607     vnf_list_parser.set_defaults(func=vnf_list)
1608     
1609     vnf_delete_parser = subparsers.add_parser('vnf-delete', parents=[parent_parser], help="deletes a vnf from the catalogue")
1610     vnf_delete_parser.add_argument("name", action="store", help="name or uuid of the VNF to be deleted")
1611     vnf_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1612     vnf_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1613     vnf_delete_parser.set_defaults(func=vnf_delete)
1614     
1615     scenario_create_parser = subparsers.add_parser('scenario-create', parents=[parent_parser], help="adds a scenario into the OPENMANO DB")
1616     scenario_create_parser.add_argument("file", action="store", help="location of the YAML file describing the scenario").completer = FilesCompleter
1617     scenario_create_parser.add_argument("--name", action="store", help="name of the scenario (if it exists in the YAML scenario, it is overwritten)")
1618     scenario_create_parser.add_argument("--description", action="store", help="description of the scenario (if it exists in the YAML scenario, it is overwritten)")
1619     scenario_create_parser.set_defaults(func=scenario_create)
1620
1621     scenario_list_parser = subparsers.add_parser('scenario-list', parents=[parent_parser], help="lists information about a scenario")
1622     scenario_list_parser.add_argument("name", nargs='?', help="name of the scenario")
1623     #scenario_list_parser.add_argument('--descriptor', help="prints the scenario descriptor", action="store_true")
1624     scenario_list_parser.add_argument("-a", "--all", action="store_true", help="shows all scenarios, not only the owned or public ones")
1625     scenario_list_parser.set_defaults(func=scenario_list)
1626     
1627     scenario_delete_parser = subparsers.add_parser('scenario-delete', parents=[parent_parser], help="deletes a scenario from the OPENMANO DB")
1628     scenario_delete_parser.add_argument("name", action="store", help="name or uuid of the scenario to be deleted")
1629     scenario_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1630     scenario_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1631     scenario_delete_parser.set_defaults(func=scenario_delete)
1632
1633     scenario_deploy_parser = subparsers.add_parser('scenario-deploy', parents=[parent_parser], help="deploys a scenario")
1634     scenario_deploy_parser.add_argument("scenario", action="store", help="name or uuid of the scenario to be deployed")
1635     scenario_deploy_parser.add_argument("name", action="store", help="name of the instance")
1636     scenario_deploy_parser.add_argument("--nostart", action="store_true", help="does not start the vms, just reserve resources")
1637     scenario_deploy_parser.add_argument("--datacenter", action="store", help="specifies the datacenter. Needed if several datacenters are available")
1638     scenario_deploy_parser.add_argument("--description", action="store", help="description of the instance")
1639     scenario_deploy_parser.set_defaults(func=scenario_deploy)
1640     
1641     scenario_deploy_parser = subparsers.add_parser('scenario-verify', help="verifies if a scenario can be deployed (deploys it and deletes it)")
1642     scenario_deploy_parser.add_argument("scenario", action="store", help="name or uuid of the scenario to be verified")
1643     scenario_deploy_parser.add_argument('--debug', '-d', action='store_true', help="show debug information")
1644     scenario_deploy_parser.set_defaults(func=scenario_verify)
1645     
1646     instance_scenario_create_parser = subparsers.add_parser('instance-scenario-create', parents=[parent_parser], help="deploys a scenario")
1647     instance_scenario_create_parser.add_argument("file", nargs='?', help="descriptor of the instance. Must be a file or yaml/json text")
1648     instance_scenario_create_parser.add_argument("--scenario", action="store", help="name or uuid of the scenario to be deployed")
1649     instance_scenario_create_parser.add_argument("--name", action="store", help="name of the instance")
1650     instance_scenario_create_parser.add_argument("--nostart", action="store_true", help="does not start the vms, just reserve resources")
1651     instance_scenario_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter. Needed if several datacenters are available")
1652     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")
1653     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")
1654     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")
1655     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")
1656     instance_scenario_create_parser.add_argument("--description", action="store", help="description of the instance")
1657     instance_scenario_create_parser.set_defaults(func=instance_create)
1658
1659     instance_scenario_list_parser = subparsers.add_parser('instance-scenario-list', parents=[parent_parser], help="lists information about a scenario instance")
1660     instance_scenario_list_parser.add_argument("name", nargs='?', help="name of the scenario instance")
1661     instance_scenario_list_parser.add_argument("-a", "--all", action="store_true", help="shows all instance-scenarios, not only the owned")
1662     instance_scenario_list_parser.set_defaults(func=instance_scenario_list)
1663
1664     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)")
1665     instance_scenario_delete_parser.add_argument("name", action="store", help="name or uuid of the scenario instance to be deleted")
1666     instance_scenario_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1667     instance_scenario_delete_parser.add_argument("-a", "--all", action="store_true", help="allow delete not owned or privated one")
1668     instance_scenario_delete_parser.set_defaults(func=instance_scenario_delete)
1669     
1670     instance_scenario_action_parser = subparsers.add_parser('instance-scenario-action', parents=[parent_parser], help="invoke an action over part or the whole scenario instance")
1671     instance_scenario_action_parser.add_argument("name", action="store", help="name or uuid of the scenario instance")
1672     instance_scenario_action_parser.add_argument("action", action="store", type=str, \
1673             choices=["start","pause","resume","shutoff","shutdown","forceOff","rebuild","reboot", "console"],\
1674             help="action to send")
1675     instance_scenario_action_parser.add_argument("param", nargs='?', help="addional param of the action. e.g. console type (novnc, ...), reboot type (TODO)")
1676     instance_scenario_action_parser.add_argument("--vnf", action="append", help="VNF to act on (can use several entries)")
1677     instance_scenario_action_parser.add_argument("--vm", action="append", help="VM to act on (can use several entries)")
1678     instance_scenario_action_parser.set_defaults(func=instance_scenario_action)
1679
1680     #instance_scenario_status_parser = subparsers.add_parser('instance-scenario-status', help="show the status of a scenario instance")
1681     #instance_scenario_status_parser.add_argument("name", action="store", help="name or uuid of the scenario instance")
1682     #instance_scenario_status_parser.set_defaults(func=instance_scenario_status)
1683     
1684     tenant_create_parser = subparsers.add_parser('tenant-create', parents=[parent_parser], help="creates a new tenant")
1685     tenant_create_parser.add_argument("name", action="store", help="name for the tenant")
1686     tenant_create_parser.add_argument("--description", action="store", help="description of the tenant")
1687     tenant_create_parser.set_defaults(func=tenant_create)
1688
1689     tenant_delete_parser = subparsers.add_parser('tenant-delete', parents=[parent_parser], help="deletes a tenant from the catalogue")
1690     tenant_delete_parser.add_argument("name", action="store", help="name or uuid of the tenant to be deleted")
1691     tenant_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1692     tenant_delete_parser.set_defaults(func=tenant_delete)
1693
1694     tenant_list_parser = subparsers.add_parser('tenant-list', parents=[parent_parser], help="lists information about a tenant")
1695     tenant_list_parser.add_argument("name", nargs='?', help="name or uuid of the tenant")
1696     tenant_list_parser.set_defaults(func=tenant_list)
1697
1698     element_edit_parser = subparsers.add_parser('tenant-edit', parents=[parent_parser], help="edits one tenant")
1699     element_edit_parser.add_argument("name", help="name or uuid of the tenant")
1700     element_edit_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter
1701     element_edit_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1702     element_edit_parser.set_defaults(func=element_edit, element='tenants')
1703
1704     datacenter_create_parser = subparsers.add_parser('datacenter-create', parents=[parent_parser], help="creates a new datacenter")
1705     datacenter_create_parser.add_argument("name", action="store", help="name for the datacenter")
1706     datacenter_create_parser.add_argument("url", action="store", help="url for the datacenter")
1707     datacenter_create_parser.add_argument("--url_admin", action="store", help="url for administration for the datacenter")
1708     datacenter_create_parser.add_argument("--type", action="store", help="datacenter type: openstack or openvim (default)")
1709     datacenter_create_parser.add_argument("--config", action="store", help="aditional configuration in json/yaml format")
1710     datacenter_create_parser.add_argument("--description", action="store", help="description of the datacenter")
1711     datacenter_create_parser.add_argument("--sdn-controller", action="store", help="Name or uuid of the SDN controller to be used", dest='sdn_controller')
1712     datacenter_create_parser.set_defaults(func=datacenter_create)
1713
1714     datacenter_delete_parser = subparsers.add_parser('datacenter-delete', parents=[parent_parser], help="deletes a datacenter from the catalogue")
1715     datacenter_delete_parser.add_argument("name", action="store", help="name or uuid of the datacenter to be deleted")
1716     datacenter_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1717     datacenter_delete_parser.set_defaults(func=datacenter_delete)
1718
1719     datacenter_edit_parser = subparsers.add_parser('datacenter-edit', parents=[parent_parser], help="Edit datacenter")
1720     datacenter_edit_parser.add_argument("name", help="name or uuid of the datacenter")
1721     datacenter_edit_parser.add_argument("--file", help="json/yaml text or file with the changes").completer = FilesCompleter
1722     datacenter_edit_parser.add_argument("--sdn-controller", action="store",
1723                                           help="Name or uuid of the SDN controller to be used. Specify 'null' to clear entry", dest='sdn_controller')
1724     datacenter_edit_parser.add_argument("-f", "--force", action="store_true", help="do not prompt for confirmation")
1725     datacenter_edit_parser.set_defaults(func=datacenter_edit)
1726
1727     datacenter_list_parser = subparsers.add_parser('datacenter-list', parents=[parent_parser], help="lists information about a datacenter")
1728     datacenter_list_parser.add_argument("name", nargs='?', help="name or uuid of the datacenter")
1729     datacenter_list_parser.add_argument("-a", "--all", action="store_true", help="shows all datacenters, not only datacenters attached to tenant")
1730     datacenter_list_parser.set_defaults(func=datacenter_list)
1731
1732     datacenter_attach_parser = subparsers.add_parser('datacenter-attach', parents=[parent_parser], help="associates a datacenter to the operating tenant")
1733     datacenter_attach_parser.add_argument("name", help="name or uuid of the datacenter")
1734     datacenter_attach_parser.add_argument('--vim-tenant-id', action='store', help="specify a datacenter tenant to use. A new one is created by default")
1735     datacenter_attach_parser.add_argument('--vim-tenant-name', action='store', help="specify a datacenter tenant name.")
1736     datacenter_attach_parser.add_argument("--user", action="store", help="user credentials for the datacenter")
1737     datacenter_attach_parser.add_argument("--password", action="store", help="password credentials for the datacenter")
1738     datacenter_attach_parser.add_argument("--config", action="store", help="aditional configuration in json/yaml format")
1739     datacenter_attach_parser.set_defaults(func=datacenter_attach)
1740
1741     datacenter_edit_vim_tenant_parser = subparsers.add_parser('datacenter-edit-vim-tenant', parents=[parent_parser],
1742                                                      help="Edit the association of a datacenter to the operating tenant")
1743     datacenter_edit_vim_tenant_parser.add_argument("name", help="name or uuid of the datacenter")
1744     datacenter_edit_vim_tenant_parser.add_argument('--vim-tenant-id', action='store',
1745                                           help="specify a datacenter tenant to use. A new one is created by default")
1746     datacenter_edit_vim_tenant_parser.add_argument('--vim-tenant-name', action='store', help="specify a datacenter tenant name.")
1747     datacenter_edit_vim_tenant_parser.add_argument("--user", action="store", help="user credentials for the datacenter")
1748     datacenter_edit_vim_tenant_parser.add_argument("--password", action="store", help="password credentials for the datacenter")
1749     datacenter_edit_vim_tenant_parser.add_argument("--config", action="store",
1750                                           help="aditional configuration in json/yaml format")
1751     datacenter_edit_vim_tenant_parser.set_defaults(func=datacenter_edit_vim_tenant)
1752
1753     datacenter_detach_parser = subparsers.add_parser('datacenter-detach', parents=[parent_parser], help="removes the association between a datacenter and the operating tenant")
1754     datacenter_detach_parser.add_argument("name", help="name or uuid of the datacenter")
1755     datacenter_detach_parser.add_argument("-a", "--all", action="store_true", help="removes all associations from this datacenter")
1756     datacenter_detach_parser.set_defaults(func=datacenter_detach)
1757
1758     #=======================datacenter_sdn_port_mapping_xxx section=======================
1759     #datacenter_sdn_port_mapping_set
1760     datacenter_sdn_port_mapping_set_parser = subparsers.add_parser('datacenter-sdn-port-mapping-set',
1761                                                                    parents=[parent_parser],
1762                                                                    help="Load a file with the mapping of physical ports "
1763                                                                         "and the ports of the dataplaneswitch controlled "
1764                                                                         "by a datacenter")
1765     datacenter_sdn_port_mapping_set_parser.add_argument("name", action="store", help="specifies the datacenter")
1766     datacenter_sdn_port_mapping_set_parser.add_argument("file",
1767                                                         help="json/yaml text or file with the port mapping").completer = FilesCompleter
1768     datacenter_sdn_port_mapping_set_parser.add_argument("-f", "--force", action="store_true",
1769                                                           help="forces overwriting without asking")
1770     datacenter_sdn_port_mapping_set_parser.set_defaults(func=datacenter_sdn_port_mapping_set)
1771
1772     #datacenter_sdn_port_mapping_list
1773     datacenter_sdn_port_mapping_list_parser = subparsers.add_parser('datacenter-sdn-port-mapping-list',
1774                                                                     parents=[parent_parser],
1775                                                                     help="Show the SDN port mapping in a datacenter")
1776     datacenter_sdn_port_mapping_list_parser.add_argument("name", action="store", help="specifies the datacenter")
1777     datacenter_sdn_port_mapping_list_parser.set_defaults(func=datacenter_sdn_port_mapping_list)
1778
1779     # datacenter_sdn_port_mapping_clear
1780     datacenter_sdn_port_mapping_clear_parser = subparsers.add_parser('datacenter-sdn-port-mapping-clear',
1781                                                                     parents=[parent_parser],
1782                                                                     help="Clean the the SDN port mapping in a datacenter")
1783     datacenter_sdn_port_mapping_clear_parser.add_argument("name", action="store",
1784                                                          help="specifies the datacenter")
1785     datacenter_sdn_port_mapping_clear_parser.add_argument("-f", "--force", action="store_true",
1786                                               help="forces clearing without asking")
1787     datacenter_sdn_port_mapping_clear_parser.set_defaults(func=datacenter_sdn_port_mapping_clear)
1788     # =======================
1789
1790     # =======================sdn_controller_xxx section=======================
1791     # sdn_controller_create
1792     sdn_controller_create_parser = subparsers.add_parser('sdn-controller-create', parents=[parent_parser],
1793                                                         help="Creates an SDN controller entity within RO")
1794     sdn_controller_create_parser.add_argument("name", help="name of the SDN controller")
1795     sdn_controller_create_parser.add_argument("--description", action="store", help="description of the SDN controller")
1796     sdn_controller_create_parser.add_argument("--ip", action="store", help="IP of the SDN controller")
1797     sdn_controller_create_parser.add_argument("--port", action="store", help="Port of the SDN controller")
1798     sdn_controller_create_parser.add_argument("--dpid", action="store",
1799                                              help="DPID of the dataplane switch controlled by this SDN controller")
1800     sdn_controller_create_parser.add_argument("--type", action="store",
1801                                              help="Specify the SDN controller type. Valid types are 'opendaylight' and 'floodlight'")
1802     sdn_controller_create_parser.add_argument("--user", action="store", help="user credentials for the SDN controller")
1803     sdn_controller_create_parser.add_argument("--passwd", action="store", dest='password',
1804                                              help="password credentials for the SDN controller")
1805     sdn_controller_create_parser.set_defaults(func=sdn_controller_create)
1806
1807     # sdn_controller_edit
1808     sdn_controller_edit_parser = subparsers.add_parser('sdn-controller-edit', parents=[parent_parser],
1809                                                         help="Update one or more options of a SDN controller")
1810     sdn_controller_edit_parser.add_argument("name", help="name or uuid of the SDN controller", )
1811     sdn_controller_edit_parser.add_argument("--name", action="store", help="Update the name of the SDN controller",
1812                                               dest='new_name')
1813     sdn_controller_edit_parser.add_argument("--description", action="store", help="description of the SDN controller")
1814     sdn_controller_edit_parser.add_argument("--ip", action="store", help="IP of the SDN controller")
1815     sdn_controller_edit_parser.add_argument("--port", action="store", help="Port of the SDN controller")
1816     sdn_controller_edit_parser.add_argument("--dpid", action="store",
1817                                              help="DPID of the dataplane switch controlled by this SDN controller")
1818     sdn_controller_edit_parser.add_argument("--type", action="store",
1819                                              help="Specify the SDN controller type. Valid types are 'opendaylight' and 'floodlight'")
1820     sdn_controller_edit_parser.add_argument("--user", action="store", help="user credentials for the SDN controller")
1821     sdn_controller_edit_parser.add_argument("--password", action="store",
1822                                              help="password credentials for the SDN controller", dest='password')
1823     sdn_controller_edit_parser.add_argument("-f", "--force", action="store_true", help="do not prompt for confirmation")
1824     #TODO: include option --file
1825     sdn_controller_edit_parser.set_defaults(func=sdn_controller_edit)
1826
1827     #sdn_controller_list
1828     sdn_controller_list_parser = subparsers.add_parser('sdn-controller-list',
1829                                                                     parents=[parent_parser],
1830                                                                     help="List the SDN controllers")
1831     sdn_controller_list_parser.add_argument("name", nargs='?', help="name or uuid of the SDN controller")
1832     sdn_controller_list_parser.set_defaults(func=sdn_controller_list)
1833
1834     # sdn_controller_delete
1835     sdn_controller_delete_parser = subparsers.add_parser('sdn-controller-delete',
1836                                                                     parents=[parent_parser],
1837                                                                     help="Delete the the SDN controller")
1838     sdn_controller_delete_parser.add_argument("name", help="name or uuid of the SDN controller")
1839     sdn_controller_delete_parser.add_argument("-f", "--force", action="store_true", help="forces deletion without asking")
1840     sdn_controller_delete_parser.set_defaults(func=sdn_controller_delete)
1841     # =======================
1842
1843     action_dict={'net-update': 'retrieves external networks from datacenter',
1844                  'net-edit': 'edits an external network',
1845                  'net-delete': 'deletes an external network',
1846                  'net-list': 'lists external networks from a datacenter'
1847                  }
1848     for item in action_dict:
1849         datacenter_action_parser = subparsers.add_parser('datacenter-'+item, parents=[parent_parser], help=action_dict[item])
1850         datacenter_action_parser.add_argument("datacenter", help="name or uuid of the datacenter")
1851         if item=='net-edit' or item=='net-delete':
1852             datacenter_action_parser.add_argument("net", help="name or uuid of the datacenter net")
1853         if item=='net-edit':
1854             datacenter_action_parser.add_argument("file", help="json/yaml text or file with the changes").completer = FilesCompleter
1855         if item!='net-list':
1856             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1857         datacenter_action_parser.set_defaults(func=datacenter_net_action, action=item)
1858
1859
1860     action_dict={'netmap-import': 'create network senario netmap base on the datacenter networks',
1861                  'netmap-create': 'create a new network senario netmap',
1862                  'netmap-edit':   'edit name of a network senario netmap',
1863                  'netmap-delete': 'deletes a network scenario netmap (--all for clearing all)',
1864                  'netmap-list':   'list/show network scenario netmaps'
1865                  }
1866     for item in action_dict:
1867         datacenter_action_parser = subparsers.add_parser('datacenter-'+item, parents=[parent_parser], help=action_dict[item])
1868         datacenter_action_parser.add_argument("--datacenter", help="name or uuid of the datacenter")
1869         #if item=='net-add':
1870         #    datacenter_action_parser.add_argument("net", help="name of the network")
1871         if item=='netmap-delete':
1872             datacenter_action_parser.add_argument("netmap", nargs='?',help="name or uuid of the datacenter netmap to delete")
1873             datacenter_action_parser.add_argument("--all", action="store_true", help="delete all netmap of this datacenter")
1874             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1875         if item=='netmap-edit':
1876             datacenter_action_parser.add_argument("netmap", help="name or uuid of the datacenter netmap do edit")
1877             datacenter_action_parser.add_argument("file", nargs='?', help="json/yaml text or file with the changes").completer = FilesCompleter
1878             datacenter_action_parser.add_argument("--name", action='store', help="name to assign to the datacenter netmap")
1879             datacenter_action_parser.add_argument('--vim-id', action='store', help="specify vim network uuid")
1880             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1881         if item=='netmap-list':
1882             datacenter_action_parser.add_argument("netmap", nargs='?',help="name or uuid of the datacenter netmap to show")
1883         if item=='netmap-create':
1884             datacenter_action_parser.add_argument("file", nargs='?', help="json/yaml text or file descriptor with the changes").completer = FilesCompleter
1885             datacenter_action_parser.add_argument("--name", action='store', help="name to assign to the datacenter netmap, by default same as vim-name")
1886             datacenter_action_parser.add_argument('--vim-id', action='store', help="specify vim network uuid")
1887             datacenter_action_parser.add_argument('--vim-name', action='store', help="specify vim network name")
1888         if item=='netmap-import':
1889             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
1890         datacenter_action_parser.set_defaults(func=datacenter_netmap_action, action=item)
1891
1892     # =======================vim_net_sdn_xxx section=======================
1893     # vim_net_sdn_attach
1894     vim_net_sdn_attach_parser = subparsers.add_parser('vim-net-sdn-attach',
1895                                                       parents=[parent_parser],
1896                                                       help="Specify the port to access to an external network using SDN")
1897     vim_net_sdn_attach_parser.add_argument("vim_net", action="store",
1898                                                 help="Name/id of the network in the vim that will be used to connect to the external network")
1899     vim_net_sdn_attach_parser.add_argument("port", action="store", help="Specifies the port in the dataplane switch to access to the external network")
1900     vim_net_sdn_attach_parser.add_argument("--vlan", action="store", help="Specifies the vlan (if any) to use in the defined port")
1901     vim_net_sdn_attach_parser.add_argument("--mac", action="store", help="Specifies the MAC (if known) of the physical device that will be reachable by this external port")
1902     vim_net_sdn_attach_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1903     vim_net_sdn_attach_parser.set_defaults(func=vim_net_sdn_attach)
1904
1905     # vim_net_sdn_detach
1906     vim_net_sdn_detach_parser = subparsers.add_parser('vim-net-sdn-detach',
1907                                                            parents=[parent_parser],
1908                                                            help="Remove the port information to access to an external network using SDN")
1909
1910     vim_net_sdn_detach_parser.add_argument("vim_net", action="store", help="Name/id of the vim network")
1911     vim_net_sdn_detach_parser.add_argument("--id", action="store",help="Specify the uuid of the external ports from this network to be detached")
1912     vim_net_sdn_detach_parser.add_argument("--all", action="store_true", help="Detach all external ports from this network")
1913     vim_net_sdn_detach_parser.add_argument("-f", "--force", action="store_true", help="forces clearing without asking")
1914     vim_net_sdn_detach_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1915     vim_net_sdn_detach_parser.set_defaults(func=vim_net_sdn_detach)
1916     # =======================
1917
1918     for item in ("network", "tenant", "image"):
1919         if item=="network":
1920             command_name = 'vim-net'
1921         else:
1922             command_name = 'vim-'+item
1923         vim_item_list_parser = subparsers.add_parser(command_name + '-list', parents=[parent_parser], help="list the vim " + item + "s")
1924         vim_item_list_parser.add_argument("name", nargs='?', help="name or uuid of the " + item + "s")
1925         vim_item_list_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1926         vim_item_list_parser.set_defaults(func=vim_action, item=item, action="list")
1927
1928         vim_item_del_parser = subparsers.add_parser(command_name + '-delete', parents=[parent_parser], help="list the vim " + item + "s")
1929         vim_item_del_parser.add_argument("name", help="name or uuid of the " + item + "s")
1930         vim_item_del_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1931         vim_item_del_parser.set_defaults(func=vim_action, item=item, action="delete")
1932
1933         if item == "network" or item == "tenant":
1934             vim_item_create_parser = subparsers.add_parser(command_name + '-create', parents=[parent_parser], help="create a "+item+" at vim")
1935             vim_item_create_parser.add_argument("file", nargs='?', help="descriptor of the %s. Must be a file or yaml/json text" % item).completer = FilesCompleter
1936             vim_item_create_parser.add_argument("--name", action="store", help="name of the %s" % item  )
1937             vim_item_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
1938             if item=="network":
1939                 vim_item_create_parser.add_argument("--type", action="store", help="type of network, data, ptp, bridge")
1940                 vim_item_create_parser.add_argument("--shared", action="store_true", help="Private or shared")
1941                 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>'")
1942             else:
1943                 vim_item_create_parser.add_argument("--description", action="store", help="description of the %s" % item)
1944             vim_item_create_parser.set_defaults(func=vim_action, item=item, action="create")
1945
1946     argcomplete.autocomplete(main_parser)
1947     
1948     try:
1949         args = main_parser.parse_args()
1950         #logging info
1951         level = logging.CRITICAL
1952         streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1953         if "debug" in args and args.debug:
1954             level = logging.DEBUG
1955         logging.basicConfig(format=streamformat, level= level)
1956         logger = logging.getLogger('mano')
1957         logger.setLevel(level)
1958         result = args.func(args)
1959         if result == None:
1960             result = 0
1961         #for some reason it fails if call exit inside try instance. Need to call exit at the end !?
1962     except (requests.exceptions.ConnectionError):
1963         print "Connection error: not possible to contact OPENMANO-SERVER (openmanod)"
1964         result = -2
1965     except (KeyboardInterrupt):
1966         print 'Exiting openmano'
1967         result = -3
1968     except (SystemExit, ArgumentParserError):
1969         result = -4
1970     except OpenmanoCLIError as e:
1971         print str(e)
1972         result = -5
1973     
1974     #print result
1975     exit(result)
1976