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