1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openvim
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 This is the thread for the http server North API.
26 Two thread will be launched, with normal and administrative permissions.
29 __author__
="Alfonso Tierno, Gerardo Garcia, Leonardo Mirabal"
30 __date__
="$10-jul-2014 12:07:15$"
42 from netaddr
import IPNetwork
, IPAddress
, all_matching_cidrs
43 #import only if needed because not needed in test mode. To allow an easier installation import RADclass
44 from jsonschema
import validate
as js_v
, exceptions
as js_e
45 import host_thread
as ht
46 from vim_schema
import host_new_schema
, host_edit_schema
, tenant_new_schema
, \
48 flavor_new_schema
, flavor_update_schema
, \
49 image_new_schema
, image_update_schema
, \
50 server_new_schema
, server_action_schema
, network_new_schema
, network_update_schema
, \
51 port_new_schema
, port_update_schema
, openflow_controller_schema
, of_port_map_new_schema
58 global RADclass_module
59 RADclass_module
=None #RADclass module is charged only if not in test mode
63 HTTP_Bad_Request
= 400
64 HTTP_Unauthorized
= 401
67 HTTP_Method_Not_Allowed
= 405
68 HTTP_Not_Acceptable
= 406
69 HTTP_Request_Timeout
= 408
71 HTTP_Service_Unavailable
= 503
72 HTTP_Internal_Server_Error
= 500
75 hash_md5
= hashlib
.md5()
76 with
open(fname
, "rb") as f
:
77 for chunk
in iter(lambda: f
.read(4096), b
""):
78 hash_md5
.update(chunk
)
79 return hash_md5
.hexdigest()
81 def md5_string(fname
):
82 hash_md5
= hashlib
.md5()
83 hash_md5
.update(fname
)
84 return hash_md5
.hexdigest()
86 def check_extended(extended
, allow_net_attach
=False):
87 '''Makes and extra checking of extended input that cannot be done using jsonschema
89 allow_net_attach: for allowing or not the uuid field at interfaces
90 that are allowed for instance, but not for flavors
91 Return: (<0, error_text) if error; (0,None) if not error '''
92 if "numas" not in extended
: return 0, None
95 for numa
in extended
["numas"]:
99 if "cores-id" in numa
:
100 if len(numa
["cores-id"]) != numa
["cores"]:
101 return -HTTP_Bad_Request
, "different number of cores-id (%d) than cores (%d) at numa %d" % (len(numa
["cores-id"]), numa
["cores"],numaid
)
102 id_s
.extend(numa
["cores-id"])
103 if "threads" in numa
:
105 if "threads-id" in numa
:
106 if len(numa
["threads-id"]) != numa
["threads"]:
107 return -HTTP_Bad_Request
, "different number of threads-id (%d) than threads (%d) at numa %d" % (len(numa
["threads-id"]), numa
["threads"],numaid
)
108 id_s
.extend(numa
["threads-id"])
109 if "paired-threads" in numa
:
111 if "paired-threads-id" in numa
:
112 if len(numa
["paired-threads-id"]) != numa
["paired-threads"]:
113 return -HTTP_Bad_Request
, "different number of paired-threads-id (%d) than paired-threads (%d) at numa %d" % (len(numa
["paired-threads-id"]), numa
["paired-threads"],numaid
)
114 for pair
in numa
["paired-threads-id"]:
116 return -HTTP_Bad_Request
, "paired-threads-id must contain a list of two elements list at numa %d" % (numaid
)
119 return -HTTP_Service_Unavailable
, "only one of cores, threads, paired-threads are allowed in this version at numa %d" % numaid
121 if "interfaces" in numa
:
125 for interface
in numa
["interfaces"]:
126 if "uuid" in interface
and not allow_net_attach
:
127 return -HTTP_Bad_Request
, "uuid field is not allowed at numa %d interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
128 if "mac_address" in interface
and interface
["dedicated"]=="yes":
129 return -HTTP_Bad_Request
, "mac_address can not be set for dedicated (passthrough) at numa %d, interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
130 if "name" in interface
:
131 if interface
["name"] in names
:
132 return -HTTP_Bad_Request
, "name repeated at numa %d, interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
133 names
.append(interface
["name"])
134 if "vpci" in interface
:
135 if interface
["vpci"] in vpcis
:
136 return -HTTP_Bad_Request
, "vpci %s repeated at numa %d, interface %s position %d" % (interface
["vpci"], numaid
, interface
.get("name",""), ifaceid
)
137 vpcis
.append(interface
["vpci"])
141 return -HTTP_Service_Unavailable
, "only one numa can be defined in this version "
142 for a
in range(0,len(id_s
)):
144 return -HTTP_Bad_Request
, "core/thread identifiers must start at 0 and gaps are not alloed. Missing id number %d" % a
149 # dictionaries that change from HTTP API to database naming
151 http2db_id
={'id':'uuid'}
152 http2db_host
={'id':'uuid'}
153 http2db_tenant
={'id':'uuid'}
154 http2db_flavor
={'id':'uuid','imageRef':'image_id', 'size': 'image_size'}
155 http2db_image
={'id':'uuid', 'created':'created_at', 'updated':'modified_at', 'public': 'public'}
156 http2db_server
={'id':'uuid','hostId':'host_id','flavorRef':'flavor_id','imageRef':'image_id','created':'created_at'}
157 http2db_network
={'id':'uuid','provider:vlan':'vlan', 'provider:physical': 'provider'}
158 http2db_ofc
= {'id': 'uuid'}
159 http2db_port
={'id':'uuid', 'network_id':'net_id', 'mac_address':'mac', 'device_owner':'type','device_id':'instance_id','binding:switch_port':'switch_port','binding:vlan':'vlan', 'bandwidth':'Mbps'}
162 def remove_extra_items(data
, schema
):
166 if type(data
) is tuple or type(data
) is list:
168 a
= remove_extra_items(d
, schema
['items'])
169 if a
is not None: deleted
.append(a
)
170 elif type(data
) is dict:
172 for k
in data
.keys():
173 if 'patternProperties' in schema
and k
not in schema
['properties'].keys():
174 reg_ex_list
= schema
['patternProperties'].keys()
175 for reg_ex
in reg_ex_list
:
176 if not re
.match(reg_ex
, k
):
179 elif 'properties' not in schema
or k
not in schema
['properties'].keys(): # or k not in schema['patternProperties'].keys():
183 a
= remove_extra_items(data
[k
], schema
['properties'][k
])
184 if a
is not None: deleted
.append({k
:a
})
185 if len(deleted
) == 0: return None
186 elif len(deleted
) == 1: return deleted
[0]
190 def delete_nulls(var
):
191 if type(var
) is dict:
193 if var
[k
] is None: del var
[k
]
194 elif type(var
[k
]) is dict or type(var
[k
]) is list or type(var
[k
]) is tuple:
195 if delete_nulls(var
[k
]): del var
[k
]
196 if len(var
) == 0: return True
197 elif type(var
) is list or type(var
) is tuple:
199 if type(k
) is dict: delete_nulls(k
)
200 if len(var
) == 0: return True
204 class httpserver(threading
.Thread
):
205 def __init__(self
, ovim
, name
="http", host
='localhost', port
=8080, admin
=False, config_
=None):
207 Creates a new thread to attend the http connections
209 db_conn: database connection
210 name: name of this thread
211 host: ip or name where to listen
212 port: port where to listen
213 admin: if this has privileges of administrator or not
214 config_: unless the first thread must be provided. It is a global dictionary where to allocate the self variable
220 if config_
is not None:
222 if 'http_threads' not in config_dic
:
223 config_dic
['http_threads'] = {}
224 threading
.Thread
.__init
__(self
)
227 self
.db
= ovim
.db
#TODO OVIM remove
230 if name
in config_dic
:
231 print "httpserver Warning!!! Onether thread with the same name", name
233 while name
+str(n
) in config_dic
:
237 self
.url_preffix
= 'http://' + self
.host
+ ':' + str(self
.port
) + url_base
238 config_dic
['http_threads'][name
] = self
240 #Ensure that when the main program exits the thread will also exit
243 self
.logger
= logging
.getLogger("openvim.http")
246 bottle
.run(host
=self
.host
, port
=self
.port
, debug
=True) #quiet=True
248 def gethost(self
, host_id
):
249 result
, content
= self
.db
.get_host(host_id
)
251 print "httpserver.gethost error %d %s" % (result
, content
)
252 bottle
.abort(-result
, content
)
254 print "httpserver.gethost host '%s' not found" % host_id
255 bottle
.abort(HTTP_Not_Found
, content
)
257 data
={'host' : content
}
258 convert_boolean(content
, ('admin_state_up',) )
259 change_keys_http2db(content
, http2db_host
, reverse
=True)
261 return format_out(data
)
263 @bottle.route(url_base
+ '/', method
='GET')
266 return 'works' #TODO: put links or redirection to /openvim???
272 def change_keys_http2db(data
, http_db
, reverse
=False):
273 '''Change keys of dictionary data according to the key_dict values
274 This allow change from http interface names to database names.
275 When reverse is True, the change is otherwise
277 data: can be a dictionary or a list
278 http_db: is a dictionary with hhtp names as keys and database names as value
279 reverse: by default change is done from http API to database. If True change is done otherwise
280 Return: None, but data is modified'''
281 if type(data
) is tuple or type(data
) is list:
283 change_keys_http2db(d
, http_db
, reverse
)
284 elif type(data
) is dict or type(data
) is bottle
.FormsDict
:
286 for k
,v
in http_db
.items():
287 if v
in data
: data
[k
]=data
.pop(v
)
289 for k
,v
in http_db
.items():
290 if k
in data
: data
[v
]=data
.pop(k
)
294 def format_out(data
):
295 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
296 if 'application/yaml' in bottle
.request
.headers
.get('Accept'):
297 bottle
.response
.content_type
='application/yaml'
298 return yaml
.safe_dump(data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) #, canonical=True, default_style='"'
299 else: #by default json
300 bottle
.response
.content_type
='application/json'
301 #return data #json no style
302 return json
.dumps(data
, indent
=4) + "\n"
304 def format_in(schema
):
306 error_text
= "Invalid header format "
307 format_type
= bottle
.request
.headers
.get('Content-Type', 'application/json')
308 if 'application/json' in format_type
:
309 error_text
= "Invalid json format "
310 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
311 client_data
= json
.load(bottle
.request
.body
)
312 #client_data = bottle.request.json()
313 elif 'application/yaml' in format_type
:
314 error_text
= "Invalid yaml format "
315 client_data
= yaml
.load(bottle
.request
.body
)
316 elif format_type
== 'application/xml':
317 bottle
.abort(501, "Content-Type: application/xml not supported yet.")
319 print "HTTP HEADERS: " + str(bottle
.request
.headers
.items())
320 bottle
.abort(HTTP_Not_Acceptable
, 'Content-Type ' + str(format_type
) + ' not supported.')
322 #if client_data == None:
323 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
327 #print "HTTP input data: ", str(client_data)
328 error_text
= "Invalid content "
329 js_v(client_data
, schema
)
332 except (ValueError, yaml
.YAMLError
) as exc
:
333 error_text
+= str(exc
)
335 bottle
.abort(HTTP_Bad_Request
, error_text
)
336 except js_e
.ValidationError
as exc
:
337 print "HTTP validate_in error, jsonschema exception ", exc
.message
, "at", exc
.path
338 print " CONTENT: " + str(bottle
.request
.body
.readlines())
340 if len(exc
.path
)>0: error_pos
=" at '" + ":".join(map(str, exc
.path
)) + "'"
341 bottle
.abort(HTTP_Bad_Request
, error_text
+ error_pos
+": "+exc
.message
)
343 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
346 def filter_query_string(qs
, http2db
, allowed
):
347 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
349 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
350 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
351 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
352 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
353 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
354 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
355 limit: limit dictated by user with the query string 'limit'. 100 by default
356 abort if not permitted, using bottel.abort
361 if type(qs
) is not bottle
.FormsDict
:
362 print '!!!!!!!!!!!!!!invalid query string not a dictionary'
363 # bottle.abort(HTTP_Internal_Server_Error, "call programmer")
367 select
+= qs
.getall(k
)
370 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field=" + v
+ "'")
375 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit=" + qs
[k
] + "'")
378 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '" + k
+ "=" + qs
[k
] + "'")
383 if len(select
) == 0: select
+= allowed
384 # change from http api to database naming
385 for i
in range(0, len(select
)):
388 select
[i
] = http2db
[k
]
389 change_keys_http2db(where
, http2db
)
390 # print "filter_query_string", select,where,limit
392 return select
, where
, limit
394 def convert_bandwidth(data
, reverse
=False):
395 '''Check the field bandwidth recursively and when found, it removes units and convert to number
396 It assumes that bandwidth is well formed
398 'data': dictionary bottle.FormsDict variable to be checked. None or empty is considered valid
399 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
403 if type(data
) is dict:
404 for k
in data
.keys():
405 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
406 convert_bandwidth(data
[k
], reverse
)
407 if "bandwidth" in data
:
409 value
=str(data
["bandwidth"])
411 pos
= value
.find("bps")
413 if value
[pos
-1]=="G": data
["bandwidth"] = int(data
["bandwidth"][:pos
-1]) * 1000
414 elif value
[pos
-1]=="k": data
["bandwidth"]= int(data
["bandwidth"][:pos
-1]) / 1000
415 else: data
["bandwidth"]= int(data
["bandwidth"][:pos
-1])
417 value
= int(data
["bandwidth"])
418 if value
% 1000 == 0: data
["bandwidth"]=str(value
/1000) + " Gbps"
419 else: data
["bandwidth"]=str(value
) + " Mbps"
421 print "convert_bandwidth exception for type", type(data
["bandwidth"]), " data", data
["bandwidth"]
423 if type(data
) is tuple or type(data
) is list:
425 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
426 convert_bandwidth(k
, reverse
)
428 def convert_boolean(data
, items
): #TODO OVIM delete
429 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
430 It assumes that bandwidth is well formed
432 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
433 'items': tuple of keys to convert
437 if type(data
) is dict:
438 for k
in data
.keys():
439 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
440 convert_boolean(data
[k
], items
)
442 if type(data
[k
]) is str:
443 if data
[k
]=="false": data
[k
]=False
444 elif data
[k
]=="true": data
[k
]=True
445 if type(data
) is tuple or type(data
) is list:
447 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
448 convert_boolean(k
, items
)
450 def convert_datetime2str(var
):
451 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
452 It enters recursively in the dict var finding this kind of variables
454 if type(var
) is dict:
455 for k
,v
in var
.items():
456 if type(v
) is datetime
.datetime
:
457 var
[k
]= v
.strftime('%Y-%m-%dT%H:%M:%S')
458 elif type(v
) is dict or type(v
) is list or type(v
) is tuple:
459 convert_datetime2str(v
)
460 if len(var
) == 0: return True
461 elif type(var
) is list or type(var
) is tuple:
463 convert_datetime2str(v
)
465 def check_valid_tenant(my
, tenant_id
):
468 return HTTP_Unauthorized
, "Needed admin privileges"
470 result
, _
= my
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
})
472 return HTTP_Not_Found
, "tenant '%s' not found" % tenant_id
477 Check if string value is a well-wormed url
478 :param url: string url
479 :return: True if is a valid url, False if is not well-formed
482 parsed_url
= urlparse
.urlparse(url
)
497 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
500 @bottle.hook('after_request')
502 #TODO: Alf: Is it needed??
503 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
509 @bottle.route(url_base
+ '/hosts', method
='GET')
510 def http_get_hosts():
511 return format_out(get_hosts())
515 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_host
,
516 ('id', 'name', 'description', 'status', 'admin_state_up', 'ip_name'))
518 myself
= config_dic
['http_threads'][ threading
.current_thread().name
]
519 result
, content
= myself
.db
.get_table(FROM
='hosts', SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
521 print "http_get_hosts Error", content
522 bottle
.abort(-result
, content
)
524 convert_boolean(content
, ('admin_state_up',) )
525 change_keys_http2db(content
, http2db_host
, reverse
=True)
527 row
['links'] = ( {'href': myself
.url_preffix
+ '/hosts/' + str(row
['id']), 'rel': 'bookmark'}, )
528 data
={'hosts' : content
}
531 @bottle.route(url_base
+ '/hosts/<host_id>', method
='GET')
532 def http_get_host_id(host_id
):
533 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
534 return my
.gethost(host_id
)
536 @bottle.route(url_base
+ '/hosts', method
='POST')
537 def http_post_hosts():
538 '''insert a host into the database. All resources are got and inserted'''
539 global RADclass_module
540 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
543 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
546 http_content
= format_in( host_new_schema
)
547 r
= remove_extra_items(http_content
, host_new_schema
)
548 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
549 change_keys_http2db(http_content
['host'], http2db_host
)
551 if 'host' in http_content
:
552 host
= http_content
['host']
553 if 'host-data' in http_content
:
554 host
.update(http_content
['host-data'])
556 host
= http_content
['host-data']
558 ip_name
= host
['ip_name']
560 password
= host
.get('password')
561 if host
.get('autodiscover'):
562 if not RADclass_module
:
564 RADclass_module
= imp
.find_module("RADclass")
565 except (IOError, ImportError) as e
:
566 raise ImportError("Cannot import RADclass.py Openvim not properly installed" +str(e
))
569 rad
= RADclass_module
.RADclass()
570 (return_status
, code
) = rad
.obtain_RAD(user
, password
, ip_name
)
573 if not return_status
:
574 print 'http_post_hosts ERROR obtaining RAD', code
575 bottle
.abort(HTTP_Bad_Request
, code
)
578 rad_structure
= yaml
.load(rad
.to_text())
579 print 'rad_structure\n---------------------'
580 print json
.dumps(rad_structure
, indent
=4)
581 print '---------------------'
583 WHERE_
={"family":rad_structure
['processor']['family'], 'manufacturer':rad_structure
['processor']['manufacturer'], 'version':rad_structure
['processor']['version']}
584 result
, content
= my
.db
.get_table(FROM
='host_ranking',
588 host
['ranking'] = content
[0]['ranking']
590 #error_text= "Host " + str(WHERE_)+ " not found in ranking table. Not valid for VIM management"
591 #bottle.abort(HTTP_Bad_Request, error_text)
593 warning_text
+= "Host " + str(WHERE_
)+ " not found in ranking table. Assuming lowest value 100\n"
594 host
['ranking'] = 100 #TODO: as not used in this version, set the lowest value
596 features
= rad_structure
['processor'].get('features', ())
597 host
['features'] = ",".join(features
)
600 for node
in (rad_structure
['resource topology']['nodes'] or {}).itervalues():
605 for core
in node
['cpu']['eligible_cores']:
606 eligible_cores
.extend(core
)
607 for core
in node
['cpu']['cores']:
608 for thread_id
in core
:
609 c
={'core_id': count
, 'thread_id': thread_id
}
610 if thread_id
not in eligible_cores
: c
['status'] = 'noteligible'
615 for port_k
, port_v
in node
['nics']['nic 0']['ports'].iteritems():
616 if port_v
['virtual']:
620 for port_k2
, port_v2
in node
['nics']['nic 0']['ports'].iteritems():
621 if port_v2
['virtual'] and port_v2
['PF_pci_id']==port_k
:
622 sriovs
.append({'pci':port_k2
, 'mac':port_v2
['mac'], 'source_name':port_v2
['source_name']})
624 #sort sriov according to pci and rename them to the vf number
625 new_sriovs
= sorted(sriovs
, key
=lambda k
: k
['pci'])
627 for sriov
in new_sriovs
:
628 sriov
['source_name'] = index
630 interfaces
.append ({'pci':str(port_k
), 'Mbps': port_v
['speed']/1000000, 'sriovs': new_sriovs
, 'mac':port_v
['mac'], 'source_name':port_v
['source_name']})
631 memory
=node
['memory']['node_size'] / (1024*1024*1024)
632 #memory=get_next_2pow(node['memory']['hugepage_nr'])
633 host
['numas'].append( {'numa_socket': node
['id'], 'hugepages': node
['memory']['hugepage_nr'], 'memory':memory
, 'interfaces': interfaces
, 'cores': cores
} )
634 # print json.dumps(host, indent=4)
635 # insert in data base
636 if "created_at" in host
:
637 del host
["created_at"]
638 for numa
in host
.get("numas", ()):
639 if "hugepages_consumed" in numa
:
640 del numa
["hugepages_consumed"]
641 for core
in numa
.get("cores", ()):
642 if "instance_id" in core
:
643 del core
["instance_id"]
644 if "v_thread_id" in core
:
645 del core
["v_thread_id"]
646 result
, content
= my
.db
.new_host(host
)
648 if content
['admin_state_up']:
650 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
651 host_develop_mode
= True if config_dic
['mode']=='development' else False
652 host_develop_bridge_iface
= config_dic
.get('development_bridge', None)
653 thread
= ht
.host_thread(name
=host
.get('name',ip_name
), user
=user
, host
=ip_name
,
654 password
=host
.get('password'),
655 keyfile
=host
.get('keyfile', config_dic
["host_ssh_keyfile"]),
656 db
=config_dic
['db'], db_lock
=config_dic
['db_lock'],
657 test
=host_test_mode
, image_path
=config_dic
['host_image_path'],
658 version
=config_dic
['version'], host_id
=content
['uuid'],
659 develop_mode
=host_develop_mode
, develop_bridge_iface
=host_develop_bridge_iface
)
662 config_dic
['host_threads'][content
['uuid']] = thread
664 if config_dic
['network_type'] == 'ovs':
666 create_dhcp_ovs_bridge()
667 config_dic
['host_threads'][content
['uuid']].insert_task("new-ovsbridge")
668 # create vlxan bwt OVS controller and computes
669 create_vxlan_mesh(content
['uuid'], my
.logger
)
672 change_keys_http2db(content
, http2db_host
, reverse
=True)
673 if len(warning_text
)>0:
674 content
["warning"]= warning_text
675 data
={'host' : content
}
676 return format_out(data
)
678 bottle
.abort(HTTP_Bad_Request
, content
)
682 def delete_dhcp_ovs_bridge(vlan
, net_uuid
):
684 Delete bridges and port created during dhcp launching at openvim controller
685 :param vlan: net vlan id
686 :param net_uuid: network identifier
689 dhcp_path
= config_dic
['ovs_controller_file_path']
691 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
692 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
694 dhcp_controller
.delete_dhcp_server(vlan
, net_uuid
, dhcp_path
)
695 dhcp_controller
.delete_dhcp_port(vlan
, net_uuid
)
698 def create_dhcp_ovs_bridge():
700 Initialize bridge to allocate the dhcp server at openvim controller
703 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
704 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
706 dhcp_controller
.create_ovs_bridge()
709 def set_mac_dhcp(vm_ip
, vlan
, first_ip
, last_ip
, cidr
, mac
):
711 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
712 :param vm_ip: IP address asigned to a VM
713 :param vlan: Segmentation id
714 :param first_ip: First dhcp range ip
715 :param last_ip: Last dhcp range ip
716 :param cidr: net cidr
717 :param mac: VM vnic mac to be macthed with the IP received
721 ip_tools
= IPNetwork(cidr
)
722 cidr_len
= ip_tools
.prefixlen
723 dhcp_netmask
= str(ip_tools
.netmask
)
724 dhcp_path
= config_dic
['ovs_controller_file_path']
726 new_cidr
= [first_ip
+ '/' + str(cidr_len
)]
727 if not len(all_matching_cidrs(vm_ip
, new_cidr
)):
730 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
731 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
733 dhcp_controller
.set_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_netmask
, first_ip
, dhcp_path
)
736 def delete_mac_dhcp(vm_ip
, vlan
, mac
):
738 Delete into dhcp conf file the ip assigned to a specific MAC address
739 :param vm_ip: IP address asigned to a VM
740 :param vlan: Segmentation id
741 :param mac: VM vnic mac to be macthed with the IP received
745 dhcp_path
= config_dic
['ovs_controller_file_path']
747 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
748 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
750 dhcp_controller
.delete_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_path
)
753 def create_vxlan_mesh(host_id
, logger
=None):
755 Create vxlan mesh across all openvimc controller and computes.
756 :param host_id: Added compute node id. Anyway vlan is created by all compute nodes
757 :param logger: To log errors
760 dhcp_compute_name
= get_vxlan_interface("dhcp")
761 existing_hosts
= get_hosts()
762 if len(existing_hosts
['hosts']) > 0:
763 # vlxan mesh creation between openvim controller and computes
764 computes_available
= existing_hosts
['hosts']
766 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
767 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
769 for compute
in computes_available
:
771 if compute
['ip_name'] != 'localhost':
772 remote_ip
= socket
.gethostbyname(compute
['ip_name'])
774 remote_ip
= 'localhost'
775 except socket
.error
as e
:
777 logger
.error("Cannot get compute node remote ip from '{}'. Skipping: {}".format(
778 compute
['ip_name'], e
))
780 # vxlan ovs_controller <=> compute node
781 vxlan_interface_name
= get_vxlan_interface(compute
['id'][:8])
782 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan", dhcp_compute_name
, dhcp_controller
.host
)
783 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, remote_ip
)
784 # vxlan from others compute node to cthis ompute node
785 for compute_src
in computes_available
:
786 if compute_src
['id'] == compute
['id']:
788 config_dic
['host_threads'][compute_src
['id']].insert_task("new-vxlan",
789 vxlan_interface_name
,
792 def delete_vxlan_mesh(host_id
):
794 Create a task for remove a specific compute of the vlxan mesh
795 :param host_id: host id to be deleted.
797 existing_hosts
= get_hosts()
798 computes_available
= existing_hosts
['hosts']
800 vxlan_interface_name
= get_vxlan_interface(host_id
[:8])
802 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
803 dhcp_host
= http_controller
.ovim
.get_dhcp_controller()
805 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
806 # remove bridge from openvim controller if no more computes exist
807 if len(existing_hosts
):
808 dhcp_host
.delete_ovs_bridge()
810 for compute
in computes_available
:
811 if host_id
== compute
['id']:
814 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
815 config_dic
['host_threads'][compute
['id']].insert_task("del-vxlan", vxlan_interface_name
)
818 def get_vxlan_interface(local_uuid
):
820 Genearte a vxlan interface name
821 :param local_uuid: host id
822 :return: vlxan-8digits
824 return 'vxlan-' + local_uuid
[:8]
827 @bottle.route(url_base
+ '/hosts/<host_id>', method
='PUT')
828 def http_put_host_id(host_id
):
829 '''modify a host into the database. All resources are got and inserted'''
830 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
833 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
836 http_content
= format_in( host_edit_schema
)
837 r
= remove_extra_items(http_content
, host_edit_schema
)
838 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
839 change_keys_http2db(http_content
['host'], http2db_host
)
842 result
, content
= my
.db
.edit_host(host_id
, http_content
['host'])
844 convert_boolean(content
, ('admin_state_up',) )
845 change_keys_http2db(content
, http2db_host
, reverse
=True)
846 data
={'host' : content
}
848 if config_dic
['network_type'] == 'ovs':
849 delete_vxlan_mesh(host_id
)
850 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
853 config_dic
['host_threads'][host_id
].name
= content
.get('name',content
['ip_name'])
854 config_dic
['host_threads'][host_id
].user
= content
['user']
855 config_dic
['host_threads'][host_id
].host
= content
['ip_name']
856 config_dic
['host_threads'][host_id
].insert_task("reload")
858 if config_dic
['network_type'] == 'ovs':
859 # create mesh with new host data
860 config_dic
['host_threads'][host_id
].insert_task("new-ovsbridge")
861 create_vxlan_mesh(host_id
, my
.logger
)
864 return format_out(data
)
866 bottle
.abort(HTTP_Bad_Request
, content
)
871 @bottle.route(url_base
+ '/hosts/<host_id>', method
='DELETE')
872 def http_delete_host_id(host_id
):
873 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
876 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
877 result
, content
= my
.db
.delete_row('hosts', host_id
)
879 bottle
.abort(HTTP_Not_Found
, content
)
881 if config_dic
['network_type'] == 'ovs':
882 delete_vxlan_mesh(host_id
)
884 if host_id
in config_dic
['host_threads']:
885 if config_dic
['network_type'] == 'ovs':
886 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
887 config_dic
['host_threads'][host_id
].insert_task("exit")
889 data
={'result' : content
}
890 return format_out(data
)
892 print "http_delete_host_id error",result
, content
893 bottle
.abort(-result
, content
)
900 @bottle.route(url_base
+ '/tenants', method
='GET')
901 def http_get_tenants():
903 Retreive tenant list from DB
906 my
= config_dic
['http_threads'][threading
.current_thread().name
]
909 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_tenant
,
910 ('id', 'name', 'description', 'enabled'))
911 tenants
= my
.ovim
.get_tenants(select_
, where_
)
912 delete_nulls(tenants
)
913 change_keys_http2db(tenants
, http2db_tenant
, reverse
=True)
914 data
= {'tenants': tenants
}
915 return format_out(data
)
916 except ovim
.ovimException
as e
:
917 my
.logger
.error(str(e
), exc_info
=True)
918 bottle
.abort(e
.http_code
, str(e
))
919 except Exception as e
:
920 my
.logger
.error(str(e
), exc_info
=True)
921 bottle
.abort(HTTP_Bad_Request
, str(e
))
924 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
925 def http_get_tenant_id(tenant_id
):
927 Get tenant from DB by id
928 :param tenant_id: tenant id
931 my
= config_dic
['http_threads'][threading
.current_thread().name
]
934 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
936 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
937 data
= {'tenant': tenant
}
938 return format_out(data
)
939 except ovim
.ovimException
as e
:
940 my
.logger
.error(str(e
), exc_info
=True)
941 bottle
.abort(e
.http_code
, str(e
))
942 except Exception as e
:
943 my
.logger
.error(str(e
), exc_info
=True)
944 bottle
.abort(HTTP_Bad_Request
, str(e
))
947 @bottle.route(url_base
+ '/tenants', method
='POST')
948 def http_post_tenants():
950 Insert a tenant into the database.
953 my
= config_dic
['http_threads'][threading
.current_thread().name
]
956 http_content
= format_in(tenant_new_schema
)
957 r
= remove_extra_items(http_content
, tenant_new_schema
)
959 my
.logger
.error("http_post_tenants: Warning: remove extra items " + str(r
), exc_info
=True)
960 # insert in data base
961 tenant_id
= my
.ovim
.new_tentant(http_content
['tenant'])
962 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
963 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
965 data
= {'tenant': tenant
}
966 return format_out(data
)
967 except ovim
.ovimException
as e
:
968 my
.logger
.error(str(e
), exc_info
=True)
969 bottle
.abort(e
.http_code
, str(e
))
970 except Exception as e
:
971 my
.logger
.error(str(e
), exc_info
=True)
972 bottle
.abort(HTTP_Bad_Request
, str(e
))
975 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
976 def http_put_tenant_id(tenant_id
):
978 Update a tenantinto DB.
979 :param tenant_id: tentant id
983 my
= config_dic
['http_threads'][threading
.current_thread().name
]
986 http_content
= format_in(tenant_edit_schema
)
987 r
= remove_extra_items(http_content
, tenant_edit_schema
)
989 print "http_put_tenant_id: Warning: remove extra items ", r
990 change_keys_http2db(http_content
['tenant'], http2db_tenant
)
991 # insert in data base
992 my
.ovim
.edit_tenant(tenant_id
, http_content
['tenant'])
993 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
994 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
995 data
= {'tenant': tenant
}
996 return format_out(data
)
997 except ovim
.ovimException
as e
:
998 my
.logger
.error(str(e
), exc_info
=True)
999 bottle
.abort(e
.http_code
, str(e
))
1000 except Exception as e
:
1001 my
.logger
.error(str(e
), exc_info
=True)
1002 bottle
.abort(HTTP_Bad_Request
, str(e
))
1005 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
1006 def http_delete_tenant_id(tenant_id
):
1008 Delete a tenant from the database.
1009 :param tenant_id: tenant id
1012 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1015 content
= my
.ovim
.delete_tentant(tenant_id
)
1016 data
= {'result': content
}
1017 return format_out(data
)
1018 except ovim
.ovimException
as e
:
1019 my
.logger
.error(str(e
), exc_info
=True)
1020 bottle
.abort(e
.http_code
, str(e
))
1021 except Exception as e
:
1022 my
.logger
.error(str(e
), exc_info
=True)
1023 bottle
.abort(HTTP_Bad_Request
, str(e
))
1029 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='GET')
1030 def http_get_flavors(tenant_id
):
1031 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1032 #check valid tenant_id
1033 result
,content
= check_valid_tenant(my
, tenant_id
)
1035 bottle
.abort(result
, content
)
1037 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1038 ('id','name','description','public') )
1039 if tenant_id
=='any':
1042 from_
='tenants_flavors inner join flavors on tenants_flavors.flavor_id=flavors.uuid'
1043 where_
['tenant_id'] = tenant_id
1044 result
, content
= my
.db
.get_table(FROM
=from_
, SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
1046 print "http_get_flavors Error", content
1047 bottle
.abort(-result
, content
)
1049 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1051 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(row
['id']) ) ), 'rel':'bookmark' } ]
1052 data
={'flavors' : content
}
1053 return format_out(data
)
1055 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='GET')
1056 def http_get_flavor_id(tenant_id
, flavor_id
):
1057 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1058 #check valid tenant_id
1059 result
,content
= check_valid_tenant(my
, tenant_id
)
1061 bottle
.abort(result
, content
)
1063 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1064 ('id','name','description','ram', 'vcpus', 'extended', 'disk', 'public') )
1065 if tenant_id
=='any':
1068 from_
='tenants_flavors as tf inner join flavors as f on tf.flavor_id=f.uuid'
1069 where_
['tenant_id'] = tenant_id
1070 where_
['uuid'] = flavor_id
1071 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
=from_
, WHERE
=where_
, LIMIT
=limit_
)
1074 print "http_get_flavor_id error %d %s" % (result
, content
)
1075 bottle
.abort(-result
, content
)
1077 print "http_get_flavors_id flavor '%s' not found" % str(flavor_id
)
1078 bottle
.abort(HTTP_Not_Found
, 'flavor %s not found' % flavor_id
)
1080 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1081 if 'extended' in content
[0] and content
[0]['extended'] is not None:
1082 extended
= json
.loads(content
[0]['extended'])
1083 if 'devices' in extended
:
1084 change_keys_http2db(extended
['devices'], http2db_flavor
, reverse
=True)
1085 content
[0]['extended']=extended
1086 convert_bandwidth(content
[0], reverse
=True)
1087 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1088 data
={'flavor' : content
[0]}
1089 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1090 return format_out(data
)
1093 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='POST')
1094 def http_post_flavors(tenant_id
):
1095 '''insert a flavor into the database, and attach to tenant.'''
1096 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1097 #check valid tenant_id
1098 result
,content
= check_valid_tenant(my
, tenant_id
)
1100 bottle
.abort(result
, content
)
1101 http_content
= format_in( flavor_new_schema
)
1102 r
= remove_extra_items(http_content
, flavor_new_schema
)
1103 if r
is not None: print "http_post_flavors: Warning: remove extra items ", r
1104 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1105 extended_dict
= http_content
['flavor'].pop('extended', None)
1106 if extended_dict
is not None:
1107 result
, content
= check_extended(extended_dict
)
1109 print "http_post_flavors wrong input extended error %d %s" % (result
, content
)
1110 bottle
.abort(-result
, content
)
1112 convert_bandwidth(extended_dict
)
1113 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1114 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1115 #insert in data base
1116 result
, content
= my
.db
.new_flavor(http_content
['flavor'], tenant_id
)
1118 return http_get_flavor_id(tenant_id
, content
)
1120 print "http_psot_flavors error %d %s" % (result
, content
)
1121 bottle
.abort(-result
, content
)
1124 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='DELETE')
1125 def http_delete_flavor_id(tenant_id
, flavor_id
):
1126 '''Deletes the flavor_id of a tenant. IT removes from tenants_flavors table.'''
1127 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1128 #check valid tenant_id
1129 result
,content
= check_valid_tenant(my
, tenant_id
)
1131 bottle
.abort(result
, content
)
1133 result
, content
= my
.db
.delete_image_flavor('flavor', flavor_id
, tenant_id
)
1135 bottle
.abort(HTTP_Not_Found
, content
)
1137 data
={'result' : content
}
1138 return format_out(data
)
1140 print "http_delete_flavor_id error",result
, content
1141 bottle
.abort(-result
, content
)
1144 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>/<action>', method
='POST')
1145 def http_attach_detach_flavors(tenant_id
, flavor_id
, action
):
1146 '''attach/detach an existing flavor in this tenant. That is insert/remove at tenants_flavors table.'''
1147 #TODO alf: not tested at all!!!
1148 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1149 #check valid tenant_id
1150 result
,content
= check_valid_tenant(my
, tenant_id
)
1152 bottle
.abort(result
, content
)
1153 if tenant_id
=='any':
1154 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1156 if action
!='attach' and action
!= 'detach':
1157 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1160 #Ensure that flavor exist
1161 from_
='tenants_flavors as tf right join flavors as f on tf.flavor_id=f.uuid'
1162 where_
={'uuid': flavor_id
}
1163 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1165 if action
=='attach':
1166 text_error
="Flavor '%s' not found" % flavor_id
1168 text_error
="Flavor '%s' not found for tenant '%s'" % (flavor_id
, tenant_id
)
1169 bottle
.abort(HTTP_Not_Found
, text_error
)
1173 if action
=='attach':
1174 if flavor
['tenant_id']!=None:
1175 bottle
.abort(HTTP_Conflict
, "Flavor '%s' already attached to tenant '%s'" % (flavor_id
, tenant_id
))
1176 if flavor
['public']=='no' and not my
.admin
:
1177 #allow only attaching public flavors
1178 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private flavor")
1180 #insert in data base
1181 result
, content
= my
.db
.new_row('tenants_flavors', {'flavor_id':flavor_id
, 'tenant_id': tenant_id
})
1183 return http_get_flavor_id(tenant_id
, flavor_id
)
1185 if flavor
['tenant_id']==None:
1186 bottle
.abort(HTTP_Not_Found
, "Flavor '%s' not attached to tenant '%s'" % (flavor_id
, tenant_id
))
1187 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_flavors', WHERE
={'flavor_id':flavor_id
, 'tenant_id':tenant_id
})
1189 if flavor
['public']=='no':
1190 #try to delete the flavor completely to avoid orphan flavors, IGNORE error
1191 my
.db
.delete_row_by_dict(FROM
='flavors', WHERE
={'uuid':flavor_id
})
1192 data
={'result' : "flavor detached"}
1193 return format_out(data
)
1195 #if get here is because an error
1196 print "http_attach_detach_flavors error %d %s" % (result
, content
)
1197 bottle
.abort(-result
, content
)
1200 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='PUT')
1201 def http_put_flavor_id(tenant_id
, flavor_id
):
1202 '''update a flavor_id into the database.'''
1203 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1204 #check valid tenant_id
1205 result
,content
= check_valid_tenant(my
, tenant_id
)
1207 bottle
.abort(result
, content
)
1209 http_content
= format_in( flavor_update_schema
)
1210 r
= remove_extra_items(http_content
, flavor_update_schema
)
1211 if r
is not None: print "http_put_flavor_id: Warning: remove extra items ", r
1212 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1213 extended_dict
= http_content
['flavor'].pop('extended', None)
1214 if extended_dict
is not None:
1215 result
, content
= check_extended(extended_dict
)
1217 print "http_put_flavor_id wrong input extended error %d %s" % (result
, content
)
1218 bottle
.abort(-result
, content
)
1220 convert_bandwidth(extended_dict
)
1221 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1222 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1223 #Ensure that flavor exist
1224 where_
={'uuid': flavor_id
}
1225 if tenant_id
=='any':
1228 from_
='tenants_flavors as ti inner join flavors as i on ti.flavor_id=i.uuid'
1229 where_
['tenant_id'] = tenant_id
1230 result
, content
= my
.db
.get_table(SELECT
=('public',), FROM
=from_
, WHERE
=where_
)
1232 text_error
="Flavor '%s' not found" % flavor_id
1233 if tenant_id
!='any':
1234 text_error
+=" for tenant '%s'" % flavor_id
1235 bottle
.abort(HTTP_Not_Found
, text_error
)
1238 if content
[0]['public']=='yes' and not my
.admin
:
1239 #allow only modifications over private flavors
1240 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public flavor")
1242 #insert in data base
1243 result
, content
= my
.db
.update_rows('flavors', http_content
['flavor'], {'uuid': flavor_id
})
1246 print "http_put_flavor_id error %d %s" % (result
, content
)
1247 bottle
.abort(-result
, content
)
1250 return http_get_flavor_id(tenant_id
, flavor_id
)
1258 @bottle.route(url_base
+ '/<tenant_id>/images', method
='GET')
1259 def http_get_images(tenant_id
):
1260 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1261 #check valid tenant_id
1262 result
,content
= check_valid_tenant(my
, tenant_id
)
1264 bottle
.abort(result
, content
)
1266 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1267 ('id','name','checksum','description','path','public') )
1268 if tenant_id
=='any':
1272 from_
='tenants_images right join images on tenants_images.image_id=images.uuid'
1273 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1274 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1276 print "http_get_images Error", content
1277 bottle
.abort(-result
, content
)
1279 change_keys_http2db(content
, http2db_image
, reverse
=True)
1280 #for row in content: row['links']=[ {'href': "/".join( (my.url_preffix, tenant_id, 'images', str(row['id']) ) ), 'rel':'bookmark' } ]
1281 data
={'images' : content
}
1282 return format_out(data
)
1284 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='GET')
1285 def http_get_image_id(tenant_id
, image_id
):
1286 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1287 #check valid tenant_id
1288 result
,content
= check_valid_tenant(my
, tenant_id
)
1290 bottle
.abort(result
, content
)
1292 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1293 ('id','name','checksum','description','progress', 'status','path', 'created', 'updated','public') )
1294 if tenant_id
=='any':
1298 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1299 where_or_
= {'tenant_id': tenant_id
, 'public': "yes"}
1300 where_
['uuid'] = image_id
1301 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1304 print "http_get_images error %d %s" % (result
, content
)
1305 bottle
.abort(-result
, content
)
1307 print "http_get_images image '%s' not found" % str(image_id
)
1308 bottle
.abort(HTTP_Not_Found
, 'image %s not found' % image_id
)
1310 convert_datetime2str(content
)
1311 change_keys_http2db(content
, http2db_image
, reverse
=True)
1312 if 'metadata' in content
[0] and content
[0]['metadata'] is not None:
1313 metadata
= json
.loads(content
[0]['metadata'])
1314 content
[0]['metadata']=metadata
1315 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'images', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1316 data
={'image' : content
[0]}
1317 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1318 return format_out(data
)
1320 @bottle.route(url_base
+ '/<tenant_id>/images', method
='POST')
1321 def http_post_images(tenant_id
):
1322 '''insert a image into the database, and attach to tenant.'''
1323 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1324 #check valid tenant_id
1325 result
,content
= check_valid_tenant(my
, tenant_id
)
1327 bottle
.abort(result
, content
)
1328 http_content
= format_in(image_new_schema
)
1329 r
= remove_extra_items(http_content
, image_new_schema
)
1330 if r
is not None: print "http_post_images: Warning: remove extra items ", r
1331 change_keys_http2db(http_content
['image'], http2db_image
)
1332 metadata_dict
= http_content
['image'].pop('metadata', None)
1333 if metadata_dict
is not None:
1334 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1337 image_file
= http_content
['image'].get('path',None)
1338 parsed_url
= urlparse
.urlparse(image_file
)
1339 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "":
1340 # The path is a local file
1341 if os
.path
.exists(image_file
):
1342 http_content
['image']['checksum'] = md5(image_file
)
1344 # The path is a URL. Code should be added to download the image and calculate the checksum
1345 #http_content['image']['checksum'] = md5(downloaded_image)
1347 # Finally, only if we are in test mode and checksum has not been calculated, we calculate it from the path
1348 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
1350 if 'checksum' not in http_content
['image']:
1351 http_content
['image']['checksum'] = md5_string(image_file
)
1353 # At this point, if the path is a local file and no chechsum has been obtained yet, an error is sent back.
1354 # If it is a URL, no error is sent. Checksum will be an empty string
1355 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "" and 'checksum' not in http_content
['image']:
1356 content
= "Image file not found"
1357 print "http_post_images error: %d %s" % (HTTP_Bad_Request
, content
)
1358 bottle
.abort(HTTP_Bad_Request
, content
)
1359 except Exception as e
:
1360 print "ERROR. Unexpected exception: %s" % (str(e
))
1361 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1362 #insert in data base
1363 result
, content
= my
.db
.new_image(http_content
['image'], tenant_id
)
1365 return http_get_image_id(tenant_id
, content
)
1367 print "http_post_images error %d %s" % (result
, content
)
1368 bottle
.abort(-result
, content
)
1371 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='DELETE')
1372 def http_delete_image_id(tenant_id
, image_id
):
1373 '''Deletes the image_id of a tenant. IT removes from tenants_images table.'''
1374 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1375 #check valid tenant_id
1376 result
,content
= check_valid_tenant(my
, tenant_id
)
1378 bottle
.abort(result
, content
)
1379 result
, content
= my
.db
.delete_image_flavor('image', image_id
, tenant_id
)
1381 bottle
.abort(HTTP_Not_Found
, content
)
1383 data
={'result' : content
}
1384 return format_out(data
)
1386 print "http_delete_image_id error",result
, content
1387 bottle
.abort(-result
, content
)
1390 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>/<action>', method
='POST')
1391 def http_attach_detach_images(tenant_id
, image_id
, action
):
1392 '''attach/detach an existing image in this tenant. That is insert/remove at tenants_images table.'''
1393 #TODO alf: not tested at all!!!
1394 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1395 #check valid tenant_id
1396 result
,content
= check_valid_tenant(my
, tenant_id
)
1398 bottle
.abort(result
, content
)
1399 if tenant_id
=='any':
1400 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1402 if action
!='attach' and action
!= 'detach':
1403 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1406 #Ensure that image exist
1407 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1408 where_
={'uuid': image_id
}
1409 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1411 if action
=='attach':
1412 text_error
="Image '%s' not found" % image_id
1414 text_error
="Image '%s' not found for tenant '%s'" % (image_id
, tenant_id
)
1415 bottle
.abort(HTTP_Not_Found
, text_error
)
1419 if action
=='attach':
1420 if image
['tenant_id']!=None:
1421 bottle
.abort(HTTP_Conflict
, "Image '%s' already attached to tenant '%s'" % (image_id
, tenant_id
))
1422 if image
['public']=='no' and not my
.admin
:
1423 #allow only attaching public images
1424 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private image")
1426 #insert in data base
1427 result
, content
= my
.db
.new_row('tenants_images', {'image_id':image_id
, 'tenant_id': tenant_id
})
1429 return http_get_image_id(tenant_id
, image_id
)
1431 if image
['tenant_id']==None:
1432 bottle
.abort(HTTP_Not_Found
, "Image '%s' not attached to tenant '%s'" % (image_id
, tenant_id
))
1433 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_images', WHERE
={'image_id':image_id
, 'tenant_id':tenant_id
})
1435 if image
['public']=='no':
1436 #try to delete the image completely to avoid orphan images, IGNORE error
1437 my
.db
.delete_row_by_dict(FROM
='images', WHERE
={'uuid':image_id
})
1438 data
={'result' : "image detached"}
1439 return format_out(data
)
1441 #if get here is because an error
1442 print "http_attach_detach_images error %d %s" % (result
, content
)
1443 bottle
.abort(-result
, content
)
1446 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='PUT')
1447 def http_put_image_id(tenant_id
, image_id
):
1448 '''update a image_id into the database.'''
1449 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1450 #check valid tenant_id
1451 result
,content
= check_valid_tenant(my
, tenant_id
)
1453 bottle
.abort(result
, content
)
1455 http_content
= format_in( image_update_schema
)
1456 r
= remove_extra_items(http_content
, image_update_schema
)
1457 if r
is not None: print "http_put_image_id: Warning: remove extra items ", r
1458 change_keys_http2db(http_content
['image'], http2db_image
)
1459 metadata_dict
= http_content
['image'].pop('metadata', None)
1460 if metadata_dict
is not None:
1461 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1462 #Ensure that image exist
1463 where_
={'uuid': image_id
}
1464 if tenant_id
=='any':
1468 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1469 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1470 result
, content
= my
.db
.get_table(SELECT
=('public',), DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND")
1472 text_error
="Image '%s' not found" % image_id
1473 if tenant_id
!='any':
1474 text_error
+=" for tenant '%s'" % image_id
1475 bottle
.abort(HTTP_Not_Found
, text_error
)
1478 if content
[0]['public']=='yes' and not my
.admin
:
1479 #allow only modifications over private images
1480 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public image")
1482 #insert in data base
1483 result
, content
= my
.db
.update_rows('images', http_content
['image'], {'uuid': image_id
})
1486 print "http_put_image_id error %d %s" % (result
, content
)
1487 bottle
.abort(-result
, content
)
1490 return http_get_image_id(tenant_id
, image_id
)
1497 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='GET')
1498 def http_get_servers(tenant_id
):
1499 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1500 result
,content
= check_valid_tenant(my
, tenant_id
)
1502 bottle
.abort(result
, content
)
1505 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_server
,
1506 ('id','name','description','hostId','imageRef','flavorRef','status', 'tenant_id') )
1507 if tenant_id
!='any':
1508 where_
['tenant_id'] = tenant_id
1509 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
='instances', WHERE
=where_
, LIMIT
=limit_
)
1511 print "http_get_servers Error", content
1512 bottle
.abort(-result
, content
)
1514 change_keys_http2db(content
, http2db_server
, reverse
=True)
1516 tenant_id
= row
.pop('tenant_id')
1517 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'servers', str(row
['id']) ) ), 'rel':'bookmark' } ]
1518 data
={'servers' : content
}
1519 return format_out(data
)
1521 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='GET')
1522 def http_get_server_id(tenant_id
, server_id
):
1523 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1524 #check valid tenant_id
1525 result
,content
= check_valid_tenant(my
, tenant_id
)
1527 bottle
.abort(result
, content
)
1530 result
, content
= my
.db
.get_instance(server_id
)
1532 bottle
.abort(HTTP_Not_Found
, content
)
1534 #change image/flavor-id to id and link
1535 convert_bandwidth(content
, reverse
=True)
1536 convert_datetime2str(content
)
1537 if content
["ram"]==0 : del content
["ram"]
1538 if content
["vcpus"]==0 : del content
["vcpus"]
1539 if 'flavor_id' in content
:
1540 if content
['flavor_id'] is not None:
1541 content
['flavor'] = {'id':content
['flavor_id'],
1542 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'flavors', str(content
['flavor_id']) ) ), 'rel':'bookmark'}]
1544 del content
['flavor_id']
1545 if 'image_id' in content
:
1546 if content
['image_id'] is not None:
1547 content
['image'] = {'id':content
['image_id'],
1548 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'images', str(content
['image_id']) ) ), 'rel':'bookmark'}]
1550 del content
['image_id']
1551 change_keys_http2db(content
, http2db_server
, reverse
=True)
1552 if 'extended' in content
:
1553 if 'devices' in content
['extended']: change_keys_http2db(content
['extended']['devices'], http2db_server
, reverse
=True)
1555 data
={'server' : content
}
1556 return format_out(data
)
1558 bottle
.abort(-result
, content
)
1561 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='POST')
1562 def http_post_server_id(tenant_id
):
1563 '''deploys a new server'''
1564 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1565 #check valid tenant_id
1566 result
,content
= check_valid_tenant(my
, tenant_id
)
1568 bottle
.abort(result
, content
)
1570 if tenant_id
=='any':
1571 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1573 http_content
= format_in( server_new_schema
)
1574 r
= remove_extra_items(http_content
, server_new_schema
)
1575 if r
is not None: print "http_post_serves: Warning: remove extra items ", r
1576 change_keys_http2db(http_content
['server'], http2db_server
)
1577 extended_dict
= http_content
['server'].get('extended', None)
1578 if extended_dict
is not None:
1579 result
, content
= check_extended(extended_dict
, True)
1581 print "http_post_servers wrong input extended error %d %s" % (result
, content
)
1582 bottle
.abort(-result
, content
)
1584 convert_bandwidth(extended_dict
)
1585 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_server
)
1587 server
= http_content
['server']
1588 server_start
= server
.get('start', 'yes')
1589 server
['tenant_id'] = tenant_id
1590 #check flavor valid and take info
1591 result
, content
= my
.db
.get_table(FROM
='tenants_flavors as tf join flavors as f on tf.flavor_id=f.uuid',
1592 SELECT
=('ram','vcpus','extended'), WHERE
={'uuid':server
['flavor_id'], 'tenant_id':tenant_id
})
1594 bottle
.abort(HTTP_Not_Found
, 'flavor_id %s not found' % server
['flavor_id'])
1596 server
['flavor']=content
[0]
1597 #check image valid and take info
1598 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1599 SELECT
=('path', 'metadata', 'image_id'),
1600 WHERE
={'uuid':server
['image_id'], "status":"ACTIVE"},
1601 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'},
1605 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % server
['image_id'])
1607 for image_dict
in content
:
1608 if image_dict
.get("image_id"):
1611 # insert in data base tenants_images
1612 r2
, c2
= my
.db
.new_row('tenants_images', {'image_id': server
['image_id'], 'tenant_id': tenant_id
})
1614 bottle
.abort(HTTP_Not_Found
, 'image_id %s cannot be used. Error %s' % (server
['image_id'], c2
))
1616 server
['image']={"path": content
[0]["path"], "metadata": content
[0]["metadata"]}
1617 if "hosts_id" in server
:
1618 result
, content
= my
.db
.get_table(FROM
='hosts', SELECT
=('uuid',), WHERE
={'uuid': server
['host_id']})
1620 bottle
.abort(HTTP_Not_Found
, 'hostId %s not found' % server
['host_id'])
1622 #print json.dumps(server, indent=4)
1624 result
, content
= ht
.create_server(server
, config_dic
['db'], config_dic
['db_lock'], config_dic
['mode']=='normal')
1627 #Insert instance to database
1630 print "inserting at DB"
1632 if server_start
== 'no':
1633 content
['status'] = 'INACTIVE'
1635 for net
in http_content
['server']['networks']:
1636 if net
['type'] == 'instance:ovs':
1637 dhcp_nets_id
.append(get_network_id(net
['net_id']))
1640 new_instance_result
, new_instance
= my
.db
.new_instance(content
, nets
, ports_to_free
)
1641 if new_instance_result
< 0:
1642 print "Error http_post_servers() :", new_instance_result
, new_instance
1643 bottle
.abort(-new_instance_result
, new_instance
)
1646 print "inserted at DB"
1650 for port
in ports_to_free
:
1651 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1653 print ' http_post_servers ERROR RESTORE IFACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1657 my
.ovim
.net_update_ofc_thread(net_id
)
1658 except ovim
.ovimException
as e
:
1659 my
.logger
.error("http_post_servers, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1661 # look for dhcp ip address
1662 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "ip_address", "net_id"], WHERE
={"instance_id": new_instance
})
1665 if config_dic
.get("dhcp_server") and iface
["net_id"] in config_dic
["dhcp_nets"]:
1666 #print "dhcp insert add task"
1667 r
,c
= config_dic
['dhcp_thread'].insert_task("add", iface
["mac"])
1669 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1671 #ensure compute contain the bridge for ovs networks:
1672 if iface
.get("net_id"):
1673 server_net
= get_network_id(iface
['net_id'])
1674 if server_net
["network"].get('provider:physical', "")[:3] == 'OVS':
1675 vlan
= str(server_net
['network']['provider:vlan'])
1676 dhcp_enable
= bool(server_net
['network']['enable_dhcp'])
1677 vm_dhcp_ip
= c2
[0]["ip_address"]
1678 config_dic
['host_threads'][server
['host_id']].insert_task("create-ovs-bridge-port", vlan
)
1680 dns
= yaml
.safe_load(server_net
['network'].get("dns"))
1681 routes
= yaml
.safe_load(server_net
['network'].get("routes"))
1682 links
= yaml
.safe_load(server_net
['network'].get("links"))
1684 dhcp_firt_ip
= str(server_net
['network']['dhcp_first_ip'])
1685 dhcp_last_ip
= str(server_net
['network']['dhcp_last_ip'])
1686 dhcp_cidr
= str(server_net
['network']['cidr'])
1687 gateway
= str(server_net
['network']['gateway_ip'])
1689 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
1690 http_controller
.ovim
.launch_dhcp_server(vlan
, dhcp_firt_ip
, dhcp_last_ip
,
1691 dhcp_cidr
, gateway
, dns
, routes
)
1692 set_mac_dhcp(vm_dhcp_ip
, vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, c2
[0]['mac'])
1695 http_controller
.ovim
.launch_link_bridge_to_ovs(vlan
, gateway
, dhcp_cidr
, links
, routes
)
1699 server
['uuid'] = new_instance
1700 server_start
= server
.get('start', 'yes')
1702 if server_start
!= 'no':
1703 server
['paused'] = True if server_start
== 'paused' else False
1704 server
['action'] = {"start":None}
1705 server
['status'] = "CREATING"
1707 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1709 my
.db
.update_rows('instances', {'status':"ERROR"}, {'uuid':server
['uuid'], 'last_error':c
}, log
=True)
1711 return http_get_server_id(tenant_id
, new_instance
)
1713 bottle
.abort(HTTP_Bad_Request
, content
)
1717 def http_server_action(server_id
, tenant_id
, action
):
1718 '''Perform actions over a server as resume, reboot, terminate, ...'''
1719 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1720 server
={"uuid": server_id
, "action":action
}
1721 where
={'uuid': server_id
}
1722 if tenant_id
!='any':
1723 where
['tenant_id']= tenant_id
1724 result
, content
= my
.db
.get_table(FROM
='instances', WHERE
=where
)
1726 bottle
.abort(HTTP_Not_Found
, "server %s not found" % server_id
)
1729 print "http_post_server_action error getting data %d %s" % (result
, content
)
1730 bottle
.abort(HTTP_Internal_Server_Error
, content
)
1732 server
.update(content
[0])
1733 tenant_id
= server
["tenant_id"]
1735 #TODO check a right content
1737 if 'terminate' in action
:
1738 new_status
='DELETING'
1739 elif server
['status'] == 'ERROR': #or server['status'] == 'CREATING':
1740 if 'terminate' not in action
and 'rebuild' not in action
:
1741 bottle
.abort(HTTP_Method_Not_Allowed
, "Server is in ERROR status, must be rebuit or deleted ")
1743 # elif server['status'] == 'INACTIVE':
1744 # if 'start' not in action and 'createImage' not in action:
1745 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'INACTIVE' status is 'start'")
1747 # if 'start' in action:
1748 # new_status='CREATING'
1749 # server['paused']='no'
1750 # elif server['status'] == 'PAUSED':
1751 # if 'resume' not in action:
1752 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'PAUSED' status is 'resume'")
1754 # elif server['status'] == 'ACTIVE':
1755 # if 'pause' not in action and 'reboot'not in action and 'shutoff'not in action:
1756 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'ACTIVE' status is 'pause','reboot' or 'shutoff'")
1759 if 'start' in action
or 'createImage' in action
or 'rebuild' in action
:
1760 #check image valid and take info
1761 image_id
= server
['image_id']
1762 if 'createImage' in action
:
1763 if 'imageRef' in action
['createImage']:
1764 image_id
= action
['createImage']['imageRef']
1765 elif 'disk' in action
['createImage']:
1766 result
, content
= my
.db
.get_table(FROM
='instance_devices',
1767 SELECT
=('image_id','dev'), WHERE
={'instance_id':server
['uuid'],"type":"disk"})
1769 bottle
.abort(HTTP_Not_Found
, 'disk not found for server')
1773 if action
['createImage']['imageRef']['disk'] != None:
1774 for disk
in content
:
1775 if disk
['dev'] == action
['createImage']['imageRef']['disk']:
1776 disk_id
= disk
['image_id']
1779 bottle
.abort(HTTP_Not_Found
, 'disk %s not found for server' % action
['createImage']['imageRef']['disk'])
1782 bottle
.abort(HTTP_Not_Found
, 'more than one disk found for server' )
1786 image_id
= content
[0]['image_id']
1788 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1789 SELECT
=('path','metadata'), WHERE
={'uuid':image_id
, "status":"ACTIVE"},
1790 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'}, WHERE_AND_OR
="AND", DISTINCT
=True)
1792 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % image_id
)
1794 if content
[0]['metadata'] is not None:
1796 metadata
= json
.loads(content
[0]['metadata'])
1798 return -HTTP_Internal_Server_Error
, "Can not decode image metadata"
1799 content
[0]['metadata']=metadata
1801 content
[0]['metadata'] = {}
1802 server
['image']=content
[0]
1803 if 'createImage' in action
:
1804 action
['createImage']['source'] = {'image_id': image_id
, 'path': content
[0]['path']}
1805 if 'createImage' in action
:
1806 #Create an entry in Database for the new image
1807 new_image
={'status':'BUILD', 'progress': 0 }
1808 new_image_metadata
=content
[0]
1809 if 'metadata' in server
['image'] and server
['image']['metadata'] != None:
1810 new_image_metadata
.update(server
['image']['metadata'])
1811 new_image_metadata
= {"use_incremental":"no"}
1812 if 'metadata' in action
['createImage']:
1813 new_image_metadata
.update(action
['createImage']['metadata'])
1814 new_image
['metadata'] = json
.dumps(new_image_metadata
)
1815 new_image
['name'] = action
['createImage'].get('name', None)
1816 new_image
['description'] = action
['createImage'].get('description', None)
1817 new_image
['uuid']=my
.db
.new_uuid()
1818 if 'path' in action
['createImage']:
1819 new_image
['path'] = action
['createImage']['path']
1821 new_image
['path']="/provisional/path/" + new_image
['uuid']
1822 result
, image_uuid
= my
.db
.new_image(new_image
, tenant_id
)
1824 bottle
.abort(HTTP_Bad_Request
, 'Error: ' + image_uuid
)
1826 server
['new_image'] = new_image
1830 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1832 print "Task queue full at host ", server
['host_id']
1833 bottle
.abort(HTTP_Request_Timeout
, c
)
1834 if 'createImage' in action
and result
>= 0:
1835 return http_get_image_id(tenant_id
, image_uuid
)
1837 #Update DB only for CREATING or DELETING status
1838 data
={'result' : 'deleting in process'}
1840 if new_status
!= None and new_status
== 'DELETING':
1845 #look for dhcp ip address
1846 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "net_id"], WHERE
={"instance_id": server_id
})
1847 r
, c
= my
.db
.delete_instance(server_id
, tenant_id
, nets
, ports_to_free
, net_ovs_list
, "requested by http")
1848 for port
in ports_to_free
:
1849 r1
,c1
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1851 my
.logger
.error("http_post_server_action server deletion ERROR at resore-iface!!!! " + c1
)
1852 warn_text
+= "; Error iface '{}' cannot be restored '{}'".format(str(port
), str(e
))
1855 my
.ovim
.net_update_ofc_thread(net_id
)
1856 except ovim
.ovimException
as e
:
1857 my
.logger
.error("http_server_action, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1858 warn_text
+= "; Error openflow rules of network '{}' cannot be restore '{}'".format(net_id
, str (e
))
1860 # look for dhcp ip address
1861 if r2
>0 and config_dic
.get("dhcp_server"):
1863 if iface
["net_id"] in config_dic
["dhcp_nets"]:
1864 r
,c
= config_dic
['dhcp_thread'].insert_task("del", iface
["mac"])
1865 #print "dhcp insert del task"
1867 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1868 # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan, vm_ip, mac)
1870 for net
in net_ovs_list
:
1876 delete_dhcp_ovs_bridge(vlan
, net_id
)
1877 delete_mac_dhcp(vm_ip
, vlan
, mac
)
1879 net_data
= my
.ovim
.show_network(net_id
)
1880 if net_data
.get('links'):
1881 links
= yaml
.load(net_data
.get('links'))
1882 my
.ovim
.delete_link_bridge_to_ovs(vlan
, links
)
1884 config_dic
['host_threads'][server
['host_id']].insert_task('del-ovs-port', vlan
, net_id
)
1886 data
["result"] += warn_text
1887 return format_out(data
)
1891 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='DELETE')
1892 def http_delete_server_id(tenant_id
, server_id
):
1893 '''delete a server'''
1894 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1895 #check valid tenant_id
1896 result
,content
= check_valid_tenant(my
, tenant_id
)
1898 bottle
.abort(result
, content
)
1901 return http_server_action(server_id
, tenant_id
, {"terminate":None} )
1904 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>/action', method
='POST')
1905 def http_post_server_action(tenant_id
, server_id
):
1906 '''take an action over a server'''
1907 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1908 #check valid tenant_id
1909 result
,content
= check_valid_tenant(my
, tenant_id
)
1911 bottle
.abort(result
, content
)
1913 http_content
= format_in( server_action_schema
)
1914 #r = remove_extra_items(http_content, server_action_schema)
1915 #if r is not None: print "http_post_server_action: Warning: remove extra items ", r
1917 return http_server_action(server_id
, tenant_id
, http_content
)
1924 @bottle.route(url_base
+ '/networks', method
='GET')
1925 def http_get_networks():
1927 Get all networks available
1930 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1934 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_network
,
1935 ('id', 'name', 'tenant_id', 'type',
1936 'shared', 'provider:vlan', 'status', 'last_error',
1937 'admin_state_up', 'provider:physical'))
1938 if "tenant_id" in where_
:
1939 del where_
["tenant_id"]
1941 content
= my
.ovim
.get_networks(select_
, where_
, limit_
)
1943 delete_nulls(content
)
1944 change_keys_http2db(content
, http2db_network
, reverse
=True)
1945 data
= {'networks': content
}
1946 return format_out(data
)
1948 except ovim
.ovimException
as e
:
1949 my
.logger
.error(str(e
), exc_info
=True)
1950 bottle
.abort(e
.http_code
, str(e
))
1951 except Exception as e
:
1952 my
.logger
.error(str(e
), exc_info
=True)
1953 bottle
.abort(HTTP_Bad_Request
, str(e
))
1956 @bottle.route(url_base
+ '/networks/<network_id>', method
='GET')
1957 def http_get_network_id(network_id
):
1959 Get a network data by id
1963 data
= get_network_id(network_id
)
1964 return format_out(data
)
1967 def get_network_id(network_id
):
1969 Get network from DB by id
1970 :param network_id: network Id
1973 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1977 where_
= bottle
.request
.query
1978 content
= my
.ovim
.show_network(network_id
, where_
)
1980 change_keys_http2db(content
, http2db_network
, reverse
=True)
1981 delete_nulls(content
)
1982 data
= {'network': content
}
1984 except ovim
.ovimException
as e
:
1985 my
.logger
.error(str(e
), exc_info
=True)
1986 bottle
.abort(e
.http_code
, str(e
))
1987 except Exception as e
:
1988 my
.logger
.error(str(e
), exc_info
=True)
1989 bottle
.abort(HTTP_Bad_Request
, str(e
))
1992 @bottle.route(url_base
+ '/networks', method
='POST')
1993 def http_post_networks():
1995 Insert a network into the database.
1998 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2002 http_content
= format_in(network_new_schema
)
2003 r
= remove_extra_items(http_content
, network_new_schema
)
2005 print "http_post_networks: Warning: remove extra items ", r
2006 change_keys_http2db(http_content
['network'], http2db_network
)
2007 network
= http_content
['network']
2008 content
= my
.ovim
.new_network(network
)
2009 return format_out(get_network_id(content
))
2010 except ovim
.ovimException
as e
:
2011 my
.logger
.error(str(e
), exc_info
=True)
2012 bottle
.abort(e
.http_code
, str(e
))
2013 except Exception as e
:
2014 my
.logger
.error(str(e
), exc_info
=True)
2015 bottle
.abort(HTTP_Bad_Request
, str(e
))
2018 @bottle.route(url_base
+ '/networks/<network_id>', method
='PUT')
2019 def http_put_network_id(network_id
):
2021 Update a network_id into DB.
2022 :param network_id: network id
2025 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2029 http_content
= format_in(network_update_schema
)
2030 change_keys_http2db(http_content
['network'], http2db_network
)
2031 network
= http_content
['network']
2032 return format_out(my
.ovim
.edit_network(network_id
, network
))
2034 except ovim
.ovimException
as e
:
2035 my
.logger
.error(str(e
), exc_info
=True)
2036 bottle
.abort(e
.http_code
, str(e
))
2037 except Exception as e
:
2038 my
.logger
.error(str(e
), exc_info
=True)
2039 bottle
.abort(HTTP_Bad_Request
, str(e
))
2042 @bottle.route(url_base
+ '/networks/<network_id>', method
='DELETE')
2043 def http_delete_network_id(network_id
):
2045 Delete a network_id from the database.
2046 :param network_id: Network id
2049 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2052 # delete from the data base
2053 content
= my
.ovim
.delete_network(network_id
)
2054 data
= {'result': content
}
2055 return format_out(data
)
2057 except ovim
.ovimException
as e
:
2058 my
.logger
.error(str(e
), exc_info
=True)
2059 bottle
.abort(e
.http_code
, str(e
))
2060 except Exception as e
:
2061 my
.logger
.error(str(e
), exc_info
=True)
2062 bottle
.abort(HTTP_Bad_Request
, str(e
))
2069 @bottle.route(url_base
+ '/openflow/controller', method
='GET')
2070 def http_get_openflow_controller():
2072 Retrieve a openflow controllers list from DB.
2075 # TODO check if show a proper list
2076 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2079 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_ofc
,
2080 ('id', 'name', 'dpid', 'ip', 'port', 'type',
2081 'version', 'user', 'password'))
2083 content
= my
.ovim
.get_of_controllers(select_
, where_
)
2084 delete_nulls(content
)
2085 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2086 data
= {'ofcs': content
}
2087 return format_out(data
)
2088 except ovim
.ovimException
as e
:
2089 my
.logger
.error(str(e
), exc_info
=True)
2090 bottle
.abort(e
.http_code
, str(e
))
2091 except Exception as e
:
2092 my
.logger
.error(str(e
), exc_info
=True)
2093 bottle
.abort(HTTP_Bad_Request
, str(e
))
2096 @bottle.route(url_base
+ '/openflow/controller/<uuid>', method
='GET')
2097 def http_get_openflow_controller_id(uuid
):
2099 Get an openflow controller by dpid from DB.get_of_controllers
2101 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2105 content
= my
.ovim
.show_of_controller(uuid
)
2106 delete_nulls(content
)
2107 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2108 data
= {'ofc': content
}
2109 return format_out(data
)
2110 except ovim
.ovimException
as e
:
2111 my
.logger
.error(str(e
), exc_info
=True)
2112 bottle
.abort(e
.http_code
, str(e
))
2113 except Exception as e
:
2114 my
.logger
.error(str(e
), exc_info
=True)
2115 bottle
.abort(HTTP_Bad_Request
, str(e
))
2118 @bottle.route(url_base
+ '/openflow/controller/', method
='POST')
2119 def http_post_openflow_controller():
2121 Create a new openflow controller into DB
2124 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2127 http_content
= format_in(openflow_controller_schema
)
2128 of_c
= http_content
['ofc']
2129 uuid
= my
.ovim
.new_of_controller(of_c
)
2130 content
= my
.ovim
.show_of_controller(uuid
)
2131 delete_nulls(content
)
2132 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2133 data
= {'ofc': content
}
2134 return format_out(data
)
2135 except ovim
.ovimException
as e
:
2136 my
.logger
.error(str(e
), exc_info
=True)
2137 bottle
.abort(e
.http_code
, str(e
))
2138 except Exception as e
:
2139 my
.logger
.error(str(e
), exc_info
=True)
2140 bottle
.abort(HTTP_Bad_Request
, str(e
))
2143 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='PUT')
2144 def http_put_openflow_controller_by_id(of_controller_id
):
2146 Create an openflow controller into DB
2147 :param of_controller_id: openflow controller dpid
2150 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2153 http_content
= format_in(openflow_controller_schema
)
2154 of_c
= http_content
['ofc']
2156 content
= my
.ovim
.edit_of_controller(of_controller_id
, of_c
)
2157 delete_nulls(content
)
2158 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2159 data
= {'ofc': content
}
2160 return format_out(data
)
2161 except ovim
.ovimException
as e
:
2162 my
.logger
.error(str(e
), exc_info
=True)
2163 bottle
.abort(e
.http_code
, str(e
))
2164 except Exception as e
:
2165 my
.logger
.error(str(e
), exc_info
=True)
2166 bottle
.abort(HTTP_Bad_Request
, str(e
))
2169 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='DELETE')
2170 def http_delete_openflow_controller(of_controller_id
):
2172 Delete an openflow controller from DB.
2173 :param of_controller_id: openflow controller dpid
2176 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2179 content
= my
.ovim
.delete_of_controller(of_controller_id
)
2180 data
= {'result': content
}
2181 return format_out(data
)
2182 except ovim
.ovimException
as e
:
2183 my
.logger
.error(str(e
), exc_info
=True)
2184 bottle
.abort(e
.http_code
, str(e
))
2185 except Exception as e
:
2186 my
.logger
.error(str(e
), exc_info
=True)
2187 bottle
.abort(HTTP_Bad_Request
, str(e
))
2190 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='GET')
2191 def http_get_openflow_id(network_id
):
2193 To obtain the list of openflow rules of a network
2194 :param network_id: network id
2197 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2200 if network_id
== 'all':
2203 content
= my
.ovim
.get_openflow_rules(network_id
)
2204 data
= {'openflow-rules': content
}
2205 except ovim
.ovimException
as e
:
2206 my
.logger
.error(str(e
), exc_info
=True)
2207 bottle
.abort(e
.http_code
, str(e
))
2208 except Exception as e
:
2209 my
.logger
.error(str(e
), exc_info
=True)
2210 bottle
.abort(HTTP_Bad_Request
, str(e
))
2212 return format_out(data
)
2215 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='PUT')
2216 def http_put_openflow_id(network_id
):
2218 To make actions over the net. The action is to reinstall the openflow rules
2219 network_id can be 'all'
2220 :param network_id: network id
2223 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2226 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2228 if network_id
== 'all':
2232 result
= my
.ovim
.edit_openflow_rules(network_id
)
2233 except ovim
.ovimException
as e
:
2234 my
.logger
.error(str(e
), exc_info
=True)
2235 bottle
.abort(e
.http_code
, str(e
))
2236 except Exception as e
:
2237 my
.logger
.error(str(e
), exc_info
=True)
2238 bottle
.abort(HTTP_Bad_Request
, str(e
))
2240 data
= {'result': str(result
) + " nets updates"}
2241 return format_out(data
)
2243 @bottle.route(url_base
+ '/networks/clear/openflow/<ofc_id>', method
='DELETE')
2244 @bottle.route(url_base
+ '/networks/clear/openflow', method
='DELETE')
2245 def http_clear_openflow_rules(ofc_id
=None):
2247 To make actions over the net. The action is to delete ALL openflow rules
2250 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2253 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2255 my
.ovim
.delete_openflow_rules(ofc_id
)
2256 except ovim
.ovimException
as e
:
2257 my
.logger
.error(str(e
), exc_info
=True)
2258 bottle
.abort(e
.http_code
, str(e
))
2259 except Exception as e
:
2260 my
.logger
.error(str(e
), exc_info
=True)
2261 bottle
.abort(HTTP_Bad_Request
, str(e
))
2263 data
= {'result': " Clearing openflow rules in process"}
2264 return format_out(data
)
2266 @bottle.route(url_base
+ '/networks/openflow/ports/<ofc_id>', method
='GET')
2267 @bottle.route(url_base
+ '/networks/openflow/ports', method
='GET')
2268 def http_get_openflow_ports(ofc_id
=None):
2270 Obtain switch ports names of openflow controller
2273 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2276 ports
= my
.ovim
.get_openflow_ports(ofc_id
)
2277 data
= {'ports': ports
}
2278 except ovim
.ovimException
as e
:
2279 my
.logger
.error(str(e
), exc_info
=True)
2280 bottle
.abort(e
.http_code
, str(e
))
2281 except Exception as e
:
2282 my
.logger
.error(str(e
), exc_info
=True)
2283 bottle
.abort(HTTP_Bad_Request
, str(e
))
2285 return format_out(data
)
2291 @bottle.route(url_base
+ '/ports', method
='GET')
2292 def http_get_ports():
2294 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2295 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_port
,
2296 ('id','name','tenant_id','network_id','vpci','mac_address','device_owner','device_id',
2297 'binding:switch_port','binding:vlan','bandwidth','status','admin_state_up','ip_address') )
2299 ports
= my
.ovim
.get_ports(columns
=select_
, filter=where_
, limit
=limit_
)
2301 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2302 data
={'ports' : ports
}
2303 return format_out(data
)
2304 except ovim
.ovimException
as e
:
2305 my
.logger
.error(str(e
), exc_info
=True)
2306 bottle
.abort(e
.http_code
, str(e
))
2307 except Exception as e
:
2308 my
.logger
.error(str(e
), exc_info
=True)
2309 bottle
.abort(HTTP_Bad_Request
, str(e
))
2311 @bottle.route(url_base
+ '/ports/<port_id>', method
='GET')
2312 def http_get_port_id(port_id
):
2313 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2315 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2317 bottle
.abort(HTTP_Not_Found
, 'port %s not found' % port_id
)
2320 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2321 data
= {'port': ports
[0]}
2322 return format_out(data
)
2323 except ovim
.ovimException
as e
:
2324 my
.logger
.error(str(e
), exc_info
=True)
2325 bottle
.abort(e
.http_code
, str(e
))
2326 except Exception as e
:
2327 my
.logger
.error(str(e
), exc_info
=True)
2328 bottle
.abort(HTTP_Bad_Request
, str(e
))
2330 @bottle.route(url_base
+ '/ports', method
='POST')
2331 def http_post_ports():
2332 '''insert an external port into the database.'''
2333 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2335 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2337 http_content
= format_in( port_new_schema
)
2338 r
= remove_extra_items(http_content
, port_new_schema
)
2339 if r
is not None: print "http_post_ports: Warning: remove extra items ", r
2340 change_keys_http2db(http_content
['port'], http2db_port
)
2341 port
=http_content
['port']
2343 port_id
= my
.ovim
.new_port(port
)
2344 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2346 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' inserted but not found at database".format(port_id
))
2349 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2350 data
= {'port': ports
[0]}
2351 return format_out(data
)
2352 except ovim
.ovimException
as e
:
2353 my
.logger
.error(str(e
), exc_info
=True)
2354 bottle
.abort(e
.http_code
, str(e
))
2355 except Exception as e
:
2356 my
.logger
.error(str(e
), exc_info
=True)
2357 bottle
.abort(HTTP_Bad_Request
, str(e
))
2359 @bottle.route(url_base
+ '/ports/<port_id>', method
='PUT')
2360 def http_put_port_id(port_id
):
2361 '''update a port_id into the database.'''
2362 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2364 http_content
= format_in( port_update_schema
)
2365 change_keys_http2db(http_content
['port'], http2db_port
)
2366 port_dict
=http_content
['port']
2368 for k
in ('vlan', 'switch_port', 'mac_address', 'tenant_id'):
2369 if k
in port_dict
and not my
.admin
:
2370 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges for changing " + k
)
2373 port_id
= my
.ovim
.edit_port(port_id
, port_dict
, my
.admin
)
2374 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2376 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' edited but not found at database".format(port_id
))
2379 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2380 data
= {'port': ports
[0]}
2381 return format_out(data
)
2382 except ovim
.ovimException
as e
:
2383 my
.logger
.error(str(e
), exc_info
=True)
2384 bottle
.abort(e
.http_code
, str(e
))
2385 except Exception as e
:
2386 my
.logger
.error(str(e
), exc_info
=True)
2387 bottle
.abort(HTTP_Bad_Request
, str(e
))
2390 @bottle.route(url_base
+ '/ports/<port_id>', method
='DELETE')
2391 def http_delete_port_id(port_id
):
2392 '''delete a port_id from the database.'''
2393 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2395 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2398 result
= my
.ovim
.delete_port(port_id
)
2399 data
= {'result': result
}
2400 return format_out(data
)
2401 except ovim
.ovimException
as e
:
2402 my
.logger
.error(str(e
), exc_info
=True)
2403 bottle
.abort(e
.http_code
, str(e
))
2404 except Exception as e
:
2405 my
.logger
.error(str(e
), exc_info
=True)
2406 bottle
.abort(HTTP_Bad_Request
, str(e
))
2409 @bottle.route(url_base
+ '/openflow/mapping', method
='POST')
2410 def http_of_port_mapping():
2412 Create new compute port mapping entry
2415 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2418 http_content
= format_in(of_port_map_new_schema
)
2419 r
= remove_extra_items(http_content
, of_port_map_new_schema
)
2421 my
.logger
.error("http_of_port_mapping: Warning: remove extra items " + str(r
), exc_info
=True)
2423 # insert in data base
2424 port_mapping
= my
.ovim
.set_of_port_mapping(http_content
['of_port_mapings'])
2425 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2426 delete_nulls(port_mapping
)
2427 data
= {'of_port_mappings': port_mapping
}
2428 return format_out(data
)
2429 except ovim
.ovimException
as e
:
2430 my
.logger
.error(str(e
), exc_info
=True)
2431 bottle
.abort(e
.http_code
, str(e
))
2432 except Exception as e
:
2433 my
.logger
.error(str(e
), exc_info
=True)
2434 bottle
.abort(HTTP_Bad_Request
, str(e
))
2437 @bottle.route(url_base
+ '/openflow/mapping', method
='GET')
2438 def get_of_port_mapping():
2440 Get compute port mapping
2443 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2446 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_id
,
2447 ('id', 'ofc_id', 'region', 'compute_node', 'pci',
2448 'switch_dpid', 'switch_port', 'switch_mac'))
2449 # insert in data base
2450 port_mapping
= my
.ovim
.get_of_port_mappings(select_
, where_
)
2451 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2452 delete_nulls(port_mapping
)
2453 data
= {'of_port_mappings': port_mapping
}
2454 return format_out(data
)
2455 except ovim
.ovimException
as e
:
2456 my
.logger
.error(str(e
), exc_info
=True)
2457 bottle
.abort(e
.http_code
, str(e
))
2458 except Exception as e
:
2459 my
.logger
.error(str(e
), exc_info
=True)
2460 bottle
.abort(HTTP_Bad_Request
, str(e
))
2463 @bottle.route(url_base
+ '/openflow/mapping/<region>', method
='DELETE')
2464 def delete_of_port_mapping(region
):
2466 Insert a tenant into the database.
2469 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2472 # insert in data base
2473 db_filter
= {'region': region
}
2474 result
= my
.ovim
.clear_of_port_mapping(db_filter
)
2475 data
= {'result': result
}
2476 return format_out(data
)
2477 except ovim
.ovimException
as e
:
2478 my
.logger
.error(str(e
), exc_info
=True)
2479 bottle
.abort(e
.http_code
, str(e
))
2480 except Exception as e
:
2481 my
.logger
.error(str(e
), exc_info
=True)
2482 bottle
.abort(HTTP_Bad_Request
, str(e
))