diff options
Diffstat (limited to 'lcm/pub/nfvi/vim')
23 files changed, 1699 insertions, 0 deletions
diff --git a/lcm/pub/nfvi/vim/__init__.py b/lcm/pub/nfvi/vim/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lcm/pub/nfvi/vim/api/__init__.py b/lcm/pub/nfvi/vim/api/__init__.py new file mode 100644 index 00000000..f5e43792 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2016 ZTE Corporation. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lcm/pub/nfvi/vim/api/multivim/__init__.py b/lcm/pub/nfvi/vim/api/multivim/__init__.py new file mode 100644 index 00000000..8d66b23f --- /dev/null +++ b/lcm/pub/nfvi/vim/api/multivim/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2017 ZTE Corporation. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lcm/pub/nfvi/vim/api/multivim/api.py b/lcm/pub/nfvi/vim/api/multivim/api.py new file mode 100644 index 00000000..f3bdd30a --- /dev/null +++ b/lcm/pub/nfvi/vim/api/multivim/api.py @@ -0,0 +1,321 @@ +# Copyright 2017 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import json +import logging + +from lcm.pub.nfvi.vim.lib.vimexception import VimException +from lcm.pub.utils.restcall import req_by_msb +from lcm.pub.nfvi.vim import const + +logger = logging.getLogger(__name__) + +VIM_DRIVER_BASE_URL = "openoapi/multivim/v1" + +def call(vim_id, tenant_id, res, method, data=''): + if data and not isinstance(data, (str, unicode)): + data = json.JSONEncoder().encode(data) + url = "{base_url}/{vim_id}{tenant_id}/{res}".format( + base_url=VIM_DRIVER_BASE_URL, + vim_id=vim_id, + tenant_id="/" + tenant_id if tenant_id else "", + res=res) + ret = req_by_msb(url, method, data) + if ret[0] > 0: + raise VimException(ret[1], ret[2]) + return json.JSONDecoder().decode(ret[1]) if ret[1] else {} + +###################################################################### + +def create_image(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "images", "POST", data) + +def delete_image(vim_id, tenant_id, image_id): + return call(vim_id, tenant_id, "images/%s" % image_id, "DELETE") + +def get_image(vim_id, tenant_id, image_id): + return call(vim_id, tenant_id, "images/%s" % image_id, "GET") + +def list_image(vim_id, tenant_id): + return call(vim_id, tenant_id, "images", "GET") + +###################################################################### + +def create_network(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "networks", "POST", data) + +def delete_network(vim_id, tenant_id, network_id): + return call(vim_id, tenant_id, "networks/%s" % network_id, "DELETE") + +def get_network(vim_id, tenant_id, network_id): + return call(vim_id, tenant_id, "networks/%s" % network_id, "GET") + +def list_network(vim_id, tenant_id): + return call(vim_id, tenant_id, "networks", "GET") + +###################################################################### + +def create_subnet(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "subnets", "POST", data) + +def delete_subnet(vim_id, tenant_id, subnet_id): + return call(vim_id, tenant_id, "subnets/%s" % subnet_id, "DELETE") + +def get_subnet(vim_id, tenant_id, subnet_id): + return call(vim_id, tenant_id, "subnets/%s" % subnet_id, "GET") + +def list_subnet(vim_id, tenant_id): + return call(vim_id, tenant_id, "subnets", "GET") + +###################################################################### + +def create_port(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "ports", "POST", data) + +def delete_port(vim_id, tenant_id, port_id): + return call(vim_id, tenant_id, "ports/%s" % port_id, "DELETE") + +def get_port(vim_id, tenant_id, port_id): + return call(vim_id, tenant_id, "ports/%s" % port_id, "GET") + +def list_port(vim_id, tenant_id): + return call(vim_id, tenant_id, "ports", "GET") + +###################################################################### + +def create_flavor(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "flavors", "POST", data) + +def delete_flavor(vim_id, tenant_id, flavor_id): + return call(vim_id, tenant_id, "flavors/%s" % flavor_id, "DELETE") + +def get_flavor(vim_id, tenant_id, flavor_id): + return call(vim_id, tenant_id, "flavors/%s" % flavor_id, "GET") + +def list_flavor(vim_id, tenant_id): + return call(vim_id, tenant_id, "flavors", "GET") + +###################################################################### + +def create_vm(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "servers", "POST", data) + +def delete_vm(vim_id, tenant_id, vm_id): + return call(vim_id, tenant_id, "servers/%s" % vm_id, "DELETE") + +def get_vm(vim_id, tenant_id, vm_id): + return call(vim_id, tenant_id, "servers/%s" % vm_id, "GET") + +def list_vm(vim_id, tenant_id): + return call(vim_id, tenant_id, "servers", "GET") + +###################################################################### + +def create_volume(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "volumes", "POST", data) + +def delete_volume(vim_id, tenant_id, volume_id): + return call(vim_id, tenant_id, "volumes/%s" % volume_id, "DELETE") + +def get_volume(vim_id, tenant_id, volume_id): + return call(vim_id, tenant_id, "volumes/%s" % volume_id, "GET") + +def list_volume(vim_id, tenant_id): + return call(vim_id, tenant_id, "volumes", "GET") + +###################################################################### + +def list_tenant(vim_id, tenant_name=""): + res = "tenants" + if tenant_name: + res = "%s?name=%s" % (res, tenant_name) + return call(vim_id, "", res, "GET") + +###################################################################### + + +class MultiVimApi: + + def login(self, connect_info): + self.vim_id = connect_info["vimid"] + self.tenant_name = connect_info["tenant"] + tenants = list_tenant(self.vim_id) + for tenant in tenants["tenants"]: + if self.tenant_name == tenant["name"]: + self.tenant_id = tenant["id"] + return [0, connect_info] + raise VimException(1, "Tenant(%s) not exist" % self.tenant_name) + + def query_net(self, auth_info, net_id): + net = get_network(self.vim_id, self.tenant_id, net_id) + return [0, { + "id": net.get("id", ""), + "name": net.get("name", ""), + "status": net.get("status", ""), + "admin_state_up": net.get("admin_state_up", True), + "network_type": net.get("networkType", ""), + "physical_network": net.get("physicalNetwork", ""), + "segmentation_id": net.get("segmentationId", ""), + "tenant_id": self.tenant_id, + "tenant_name": self.tenant_name, + "subnets": net.get("subnets", []), + "shared": net.get("shared", True), + "router_external": net.get("routerExternal", "") + }] + + def query_nets(self, auth_info): + nets = list_network(self.vim_id, self.tenant_id) + return [0, {"networks": [{ + "id": net.get("id", ""), + "name": net.get("name", ""), + "status": net.get("status", ""), + "admin_state_up": net.get("admin_state_up", True), + "network_type": net.get("networkType", ""), + "physical_network": net.get("physicalNetwork", ""), + "segmentation_id": net.get("segmentationId", ""), + "tenant_id": self.tenant_id, + "tenant_name": self.tenant_name, + "subnets": net.get("subnets", []), + "shared": net.get("shared", True), + "router_external": net.get("routerExternal", "") + } for net in nets["networks"]]}] + + def query_subnet(self, auth_info, subnet_id): + subnet_info = get_subnet(self.vim_id, self.tenant_id, subnet_id) + ret = [0, {}] + ret[1]["id"] = subnet_id + ret[1]["name"] = subnet_info.get("name", "") + ret[1]["status"] = "" + ret[1]["ip_version"] = subnet_info.get("ipVersion", 4) + ret[1]["cidr"] = subnet_info.get("cidr", "") + ret[1]["allocation_pools"] = subnet_info.get("allocationPools", []) + ret[1]["enable_dhcp"] = subnet_info.get("enableDhcp", False) + ret[1]["gateway_ip"] = subnet_info.get("gatewayIp", "") + ret[1]["host_routes"] = subnet_info.get("hostRoutes", []) + ret[1]["dns_nameservers"] = subnet_info.get("dnsNameservers", []) + return ret + + def query_port(self, auth_info, port_id): + port_info = get_port(self.vim_id, self.tenant_id, port_id) + ret = [0, {}] + ret[1]["id"] = port_id + ret[1]["name"] = port_info.get("name", "") + ret[1]["network_id"] = port_info.get("networkId", "") + ret[1]["tenant_id"] = self.tenant_id, + ret[1]["ip"] = port_info.get("ip", "") + ret[1]["subnet_id"] = port_info.get("subnetId", "") + ret[1]["mac_address"] = port_info.get("macAddress", "") + ret[1]["status"] = port_info.get("status", "") + ret[1]["admin_state_up"] = port_info.get("admin_state_up", True) + ret[1]["device_id"] = port_info.get("device_id", "") + return ret + + def create_port(self, auth_info, data): + return [0, data] + + def delete_port(self, auth_info, port_id): + return [0, ""] + + def create_image(self, auth_info, data): + image_data = { + "name": data["image_name"], + "imagePath": data["image_url"], + "imageType": data["image_type"], + "containerFormat": "bare", + "visibility": "public", + "properties": [] + } + image = create_image(self.vim_id, self.tenant_id, image_data) + return [0, { + "id": image["id"], + "name": image["name"], + const.RES_TYPE_KEY: image["returnCode"]}] + + def get_image(self, auth_info, image_id): + image = get_image(self.vim_id, self.tenant_id, image_id) + return [0, { + "id": image["id"], + "name": image["name"], + "size": image["size"], + "status": image["status"]}] + + def get_images(self, auth_info): + images = list_image(self.vim_id, self.tenant_id) + return [0, {"image_list": [{ + "id": img["id"], + "name": img["name"], + "size": img["size"], + "status": img["status"] + } for img in images["images"]]}] + + def delete_image(self, auth_info, image_id): + return [0, ""] + + def create_network(self, auth_info, data): + net_data = { + "name": data["network_name"], + "shared": True, + "networkType": data["network_type"] + } + if "physical_network" in data and data['physical_network']: + net_data["physicalNetwork"] = data['physical_network'] + if "vlan_transparent" in data and data["vlan_transparent"]: + net_data["vlanTransparent"] = data["vlan_transparent"] + if "segmentation_id" in data and data['segmentation_id']: + net_data["segmentationId"] = data["segmentation_id"] + if "routerExternal" in data and data['routerExternal']: + net_data["routerExternal"] = data["routerExternal"] + net = create_network(self.vim_id, self.tenant_id, net_data) + network_id = net["id"] + ret_net = { + "status": net.get("status", ""), + "id": network_id, + "name": net.get("name", ""), + "provider:segmentation_id": net.get("segmentationId", ""), + "provider:network_type": net.get("networkType", ""), + const.RES_TYPE_KEY: net["returnCode"], + "subnet_list": [] + } + if "subnet_list" in data and data["subnet_list"]: + subnet = data["subnet_list"][0] + subnet_data = { + "networkId": network_id, + "name": subnet["subnet_name"], + "cidr": subnet["cidr"], + "ipVersion": const.IPV4, + "enableDhcp": False + } + if "ip_version" in subnet and subnet["ip_version"]: + subnet_data["ipVersion"] = int(subnet["ip_version"]) + if "enable_dhcp" in subnet and subnet["enable_dhcp"]: + subnet_data["enableDhcp"] = int(subnet["enable_dhcp"]) == const.ENABLE_DHCP + if "gateway_ip" in subnet and subnet["gateway_ip"]: + subnet_data["gatewayIp"] = subnet["gateway_ip"] + if "dns_nameservers" in subnet and subnet["dns_nameservers"]: + subnet_data["dnsNameservers"] = subnet["dns_nameservers"] + if "allocation_pools" in subnet and subnet["allocation_pools"]: + subnet_data["allocationPools"] = subnet["allocation_pools"] + if "host_routes" in subnet and subnet["host_routes"]: + subnet_data["hostRoutes"] = subnet["host_routes"] + subnet_create = create_subnet(self.vim_id, self.tenant_id, subnet_data) + ret_net["subnet_list"].append({ + "id": subnet_create["id"], + "name": subnet_create["name"], + const.RES_TYPE_KEY: net["returnCode"]}) + return [0, ret_net] + + def delete_network(self, auth_info, network_id): + return delete_network(self.vim_id, self.tenant_id, network_id) + + def delete_subnet(self, auth_info, subnet_id): + return delete_subnet(self.vim_id, self.tenant_id, subnet_id) diff --git a/lcm/pub/nfvi/vim/api/openstack/__init__.py b/lcm/pub/nfvi/vim/api/openstack/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lcm/pub/nfvi/vim/api/openstack/api.py b/lcm/pub/nfvi/vim/api/openstack/api.py new file mode 100644 index 00000000..f781e475 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/api.py @@ -0,0 +1,60 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from lcm.pub.nfvi.vim.api.openstack import auth, network, image + + +class OpenstackApi: + + def login(self, connect_info): + return auth.login(connect_info) + + def query_net(self, auth_info, net_id): + return network.query_net(auth_info, net_id) + + def query_nets(self, auth_info): + return network.query_nets(auth_info) + + def query_subnet(self, auth_info, subnet_id): + return network.query_subnet(auth_info, subnet_id) + + def query_port(self, auth_info, port_id): + return network.query_port(auth_info, port_id) + + def create_port(self, auth_info, data): + return network.create_port(auth_info, data) + + def delete_port(self, auth_info, port_id): + return network.delete_port(auth_info, port_id) + + def create_image(self, auth_info, data): + return image.create_image(auth_info, data) + + def get_image(self, auth_info, image_id): + return image.get_image(auth_info, image_id) + + def get_images(self, auth_info): + return image.get_images(auth_info) + + def delete_image(self, auth_info, image_id): + return image.delete_image(auth_info, image_id) + + def create_network(self, auth_info, data): + return network.create_network(auth_info, data) + + def delete_network(self, auth_info, network_id): + return network.delete_network(auth_info, network_id) + + def delete_subnet(self, auth_info, subnet_id): + return network.delete_subnet(auth_info, subnet_id) diff --git a/lcm/pub/nfvi/vim/api/openstack/auth.py b/lcm/pub/nfvi/vim/api/openstack/auth.py new file mode 100644 index 00000000..b662c51f --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/auth.py @@ -0,0 +1,46 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from keystoneclient.v2_0 import client + +from lcm.pub.nfvi.vim.lib.syscomm import fun_name + +logger = logging.getLogger(__name__) + +OPENSTACK_CA_CERT = '/etc/httpd/conf.d/cert/vim/ca.cert' +OPENSTACK_CLIENT_CERT = '/etc/httpd/conf.d/cert/vim/client.cert' +OPENSTACK_CLIENT_KEY = '/etc/httpd/conf.d/cert/vim/client.key' + + +def login(connect_info): + url, user = connect_info["url"], connect_info["user"] + passwd, tenant = connect_info["passwd"], connect_info["tenant"] + cacert, clientcert, clientkey = None, None, None + insecure = url.startswith('https') + logger.info( + "[%s]client.Client(auth_url='%s'," + "username='%s',password='%s'," + "tenant_name='%s',insecure=%s,cert='%s',key='%s',cacert='%s')" % + (fun_name(), url, user, passwd, tenant, insecure, clientcert, clientkey, cacert)) + connect_info["cacert"] = cacert + connect_info["clientcert"] = clientcert + connect_info["clientkey"] = clientkey + connect_info["insecure"] = insecure + connect_info["keystone"] = client.Client(auth_url=url, username=user, password=passwd, interface='public', + tenant_name=tenant, insecure=insecure, cert=clientcert, key=clientkey, + cacert=cacert, debug=True) + ret = [0, connect_info] + return ret diff --git a/lcm/pub/nfvi/vim/api/openstack/glancebase.py b/lcm/pub/nfvi/vim/api/openstack/glancebase.py new file mode 100644 index 00000000..cfcde4fd --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/glancebase.py @@ -0,0 +1,43 @@ +# Copyright 2016 ZTE Corporation. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +logger = logging.getLogger(__name__) + + +def get_glance(funname, auth_info, ver='v2'): + import glanceclient.v1.client as glanceclient1 + import glanceclient.v2.client as glanceclient2 + keystone = auth_info["keystone"] + cacert = auth_info["cacert"] + clientcert = auth_info["clientcert"] + clientkey = auth_info["clientkey"] + insecure = auth_info["insecure"] + glance_endpoint = keystone.service_catalog.url_for(service_type='image') + logger.info("[%s]call glanceclient.Client('%s',token='%s',insecure=%s,cert_file='%s',key_file='%s',cacert='%s')" + % (funname, glance_endpoint, keystone.auth_token, insecure, clientcert, clientkey, cacert)) + if 'v1' == ver: + logger.info("return glanceclient1") + return glanceclient1.Client(glance_endpoint, + token=keystone.auth_token, + insecure=insecure, + cert_file=clientcert, + key_file=clientkey, + cacert=cacert) + else: + logger.info("return glanceclient2") + return glanceclient2.Client(glance_endpoint, + token=keystone.auth_token, + insecure=insecure, + cert_file=clientcert, + key_file=clientkey, + cacert=cacert) diff --git a/lcm/pub/nfvi/vim/api/openstack/image.py b/lcm/pub/nfvi/vim/api/openstack/image.py new file mode 100644 index 00000000..2a04cb9a --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/image.py @@ -0,0 +1,106 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import sys +import traceback +import threading + +from lcm.pub.nfvi.vim.api.openstack import glancebase +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim import const + +logger = logging.getLogger(__name__) + + +class ImageUploadThread(threading.Thread): + def __init__(self, glance, image_id, image_path): + threading.Thread.__init__(self) + self.glance = glance + self.image_id = image_id + self.image_path = image_path + + def run(self): + try: + self.glance.images.upload(self.image_id, open(self.image_path, 'rb')) + except Exception as ex: + logger.error(traceback.format_exc()) + err_msg = ex.message if ex.message else str(sys.exc_info()) + logger.error("Failed to upload image(%s): %s", self.image_id, err_msg) + except: + logger.error(traceback.format_exc()) + logger.error("Failed to upload image(%s): [%s]", self.image_id, str(sys.exc_info())) + + +def create_image(auth_info, data): + ret = None + glance = glancebase.get_glance(fun_name(), auth_info) + + exist_img = [img for img in glance.images.list() if img.name == data["image_name"]] + if exist_img: + ret = [0, {"id": exist_img[0].id, "name": data["image_name"], const.RES_TYPE_KEY: const.RES_TYPE_EXIST}] + else: + img = glance.images.create( + name=data["image_name"], + disk_format=data["image_type"], + visibility='public', + container_format='bare') + ret = [0, {"id": img.id, "name": data["image_name"], const.RES_TYPE_KEY: const.RES_TYPE_NEW}] + try: + ImageUploadThread(glance, img.id, data["image_path"]).start() + except: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + return ret + + +def get_image(auth_info, image_id): + from glanceclient.exc import HTTPNotFound + glance = glancebase.get_glance(fun_name(), auth_info) + img = None + try: + img = glance.images.get(image_id) + except HTTPNotFound: + logger.warn("Exception: %s" % str(sys.exc_info())) + return [2, "Image(%s) does not exist" % image_id] + ret_img_info = get_single_image(img) + if 'status' in ret_img_info and 'deleted' == ret_img_info["status"]: + return [2, "Image(%s) is deleted" % image_id] + return [0, ret_img_info] + + +def delete_image(auth_info, image_id): + from glanceclient.exc import HTTPNotFound + glance = glancebase.get_glance(fun_name(), auth_info) + try: + glance.images.delete(image_id) + except HTTPNotFound: + logger.warn("Exception: %s" % str(sys.exc_info())) + return [0, "Image(%s) does not exist" % image_id] + return [0, "Image(%s) has been deleted" % image_id] + + +def get_images(auth_info): + glance = glancebase.get_glance(fun_name(), auth_info) + imgs = glance.images.list() + return [0, {"image_list": [get_single_image(img) for img in imgs]}] + + +def get_single_image(img): + img_size = 0 + try: + img_size = img.size / 1024 + except: + pass + return {"id": img.id, "name": img.name, "size": img_size, "status": img.status} diff --git a/lcm/pub/nfvi/vim/api/openstack/network.py b/lcm/pub/nfvi/vim/api/openstack/network.py new file mode 100644 index 00000000..ebfdf660 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/network.py @@ -0,0 +1,423 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import sys +import traceback + +from neutronclient.common.exceptions import NeutronClientException +from neutronclient.common.exceptions import NetworkNotFoundClient +from neutronclient.common.exceptions import NotFound as SubnetNotFound + +from lcm.pub.nfvi.vim.api.openstack import neutronbase +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim.api.openstack import project +from lcm.pub.nfvi.vim import const +from lcm.pub.nfvi.vim.lib.vimexception import VimException + +logger = logging.getLogger(__name__) + + +def query_net(auth_info, net_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + net = None + try: + net = neutron.show_network(net_id)["network"] + keystone = auth_info["keystone"] + tenant = keystone.tenants.get(tenant_id=net["tenant_id"]) + except NetworkNotFoundClient as e: + logger.warn("NetworkNotFoundClient: %s", e.message) + return [2, e.message] + return [0, { + "id": net["id"], + "name": net["name"], + "status": net["status"], + "admin_state_up": net["admin_state_up"], + "network_type": net["provider:network_type"], + "physical_network": net["provider:physical_network"], + "segmentation_id": net["provider:segmentation_id"], + "tenant_id": net["tenant_id"], + "tenant_name": tenant.name, + "subnets": net["subnets"], + "shared": net["shared"], + "router_external": net["router:external"] + }] + + +def query_nets(auth_info): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + keystone = auth_info["keystone"] + tenants_map = {} + tenants = keystone.tenants.list() + for tenant in tenants: + tenants_map[tenant.id] = tenant.name + + nets = neutron.list_networks() + return [0, {"networks": [{ + "id": net["id"], + "name": net["name"], + "status": net["status"], + "admin_state_up": net["admin_state_up"], + "network_type": net["provider:network_type"], + "physical_network": net["provider:physical_network"], + "segmentation_id": net["provider:segmentation_id"], + "tenant_id": net["tenant_id"], + "tenant_name": tenants_map[net["tenant_id"]] if net["tenant_id"] in tenants_map else "unknown", + "subnets": net["subnets"], + "shared": net["shared"], + "router_external": net["router:external"] + } for net in nets["networks"]]}] + + +def query_subnet(auth_info, subnet_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + subnet_info = None + try: + subnet_info = neutron.show_subnet(subnet_id)["subnet"] + except SubnetNotFound as e: + logger.warn("SubnetNotFound: %s", e.message) + return [2, e.message] + ret = [0, {}] + ret[1]["id"] = subnet_id + ret[1]["name"] = subnet_info["name"] + ret[1]["status"] = "" + ret[1]["ip_version"] = subnet_info["ip_version"] + ret[1]["cidr"] = subnet_info["cidr"] + ret[1]["allocation_pools"] = subnet_info["allocation_pools"] + ret[1]["enable_dhcp"] = subnet_info["enable_dhcp"] + ret[1]["gateway_ip"] = subnet_info["gateway_ip"] + ret[1]["host_routes"] = subnet_info["host_routes"] + ret[1]["dns_nameservers"] = subnet_info["dns_nameservers"] + return ret + + +def query_port(auth_info, port_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + port_info = None + try: + port_info = neutron.show_port(port_id)["port"] + except NeutronClientException as e: + logger.warn("NeutronClientException: %s", e.message) + return [2, e.message] + ret = [0, {}] + ret[1]["id"] = port_id + ret[1]["name"] = port_info["name"] + ret[1]["network_id"] = port_info["network_id"] + ret[1]["tenant_id"] = port_info["tenant_id"] + if "fixed_ips" in port_info and port_info["fixed_ips"]: + ret[1]["ip"] = port_info["fixed_ips"][0]["ip_address"] + ret[1]["subnet_id"] = port_info["fixed_ips"][0]["subnet_id"] + else: + ret[1]["ip"] = "" + ret[1]["subnet_id"] = "" + ret[1]["mac_address"] = port_info["mac_address"] + ret[1]["status"] = port_info["status"] + ret[1]["admin_state_up"] = port_info["admin_state_up"] + ret[1]["device_id"] = port_info["device_id"] + return ret + + +def get_subnet_id(neutron, data, network_id): + subnet_id = None + if "subnet_name" in data and data["subnet_name"]: + all_subnets = neutron.list_subnets() + filter_subnets = [subnet for subnet in all_subnets["subnets"] if subnet["name"] == data["subnet_name"] + and subnet["network_id"] == network_id] + count_filter_subnets = len(filter_subnets) + if 1 > count_filter_subnets: + logger.error("Subnet name(%s) does not exist" % data["subnet_name"]) + raise VimException("Subnet name(%s) does not exist" % data["subnet_name"]) + if 1 < count_filter_subnets: + for subnet in filter_subnets: + logger.error("subnet_id=%s", subnet["id"]) + raise VimException("%d subnet(%s) exist in network(%s)" + % (count_filter_subnets, data["subnet_name"], data["network_name"])) + subnet_id = filter_subnets[0]['id'] + else: + subnets = neutron.list_subnets() + filter_subnets = [subnet for subnet in subnets["subnets"] if subnet["network_id"] == network_id] + if filter_subnets: + subnet_id = filter_subnets[0]["id"] + return subnet_id + + +def create_port(auth_info, data): + tenant_id = project.get_tenant_id(fun_name(), auth_info, data["tenant_name"]) + + neutron_admin = neutronbase.get_neutron_default(fun_name(), auth_info) + all_nets = neutron_admin.list_networks() + filter_nets = [net for net in all_nets['networks'] if net['name'] == data["network_name"]] + sel_nets = [net for net in filter_nets if net['tenant_id'] == tenant_id or + (net['tenant_id'] != tenant_id and net['shared'])] + count_sel_nets = len(sel_nets) + if 1 > count_sel_nets: + logger.error("Network(%s) does not exist" % data["network_name"]) + raise VimException("Network(%s) does not exist" % data["network_name"]) + if 1 < count_sel_nets: + for net in sel_nets: + logger.error("net_id=%s", net["id"]) + raise VimException("%d networks(%s) exist in tenant(%s)" + % (count_sel_nets, data["network_name"], data["tenant_name"])) + network_id = sel_nets[0]['id'] + if tenant_id != sel_nets[0]['tenant_id']: + neutron = neutronbase.get_neutron_by_tenant_id(fun_name(), auth_info, sel_nets[0]['tenant_id']) + else: + neutron = neutronbase.get_neutron(fun_name(), auth_info, data["tenant_name"]) + + # get subnet id + subnet_id = get_subnet_id(neutron_admin, data, network_id) + + # check port + port_data = None + ports = neutron.list_ports() + sel_ports = [port for port in ports['ports'] if port['tenant_id'] == tenant_id + and port['network_id'] == network_id] + filter_ports = [] + for port in sel_ports: + if port['name'] == data["port_name"] and port['fixed_ips']: + for fixed_ip in port['fixed_ips']: + if fixed_ip['subnet_id'] == subnet_id: + filter_ports.append(port) + break + count_filter_ports = len(filter_ports) + if 1 < count_filter_ports: + for port in filter_ports: + logger.error("port_id=%s", port["id"]) + raise VimException("%d port(%s) exist in subnet(%s)" + % (count_filter_ports, data["port_name"], data["subnet_name"])) + if 1 == len(filter_ports): + logger.debug("Port(%s) is exist!" % data["port_name"]) + port_data = {'status': filter_ports[0]['status'], + 'id': filter_ports[0]['id'], + 'name': filter_ports[0]['name'], + 'network_id': filter_ports[0]['network_id'], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST} + return [0, port_data] + + # create port + create_param = {'port': { + 'name': data["port_name"], + 'admin_state_up': True, + 'network_id': network_id, + 'security_groups': [], + 'tenant_id': tenant_id}} + if "mac_address" in data and data["mac_address"]: + create_param['port']['mac_address'] = data["mac_address"] + if "vnic_type" in data and data["vnic_type"]: + create_param['port']['binding:vnic_type'] = data["vnic_type"] + if "bandwidth" in data and data["bandwidth"]: + create_param['port']['bandwidth'] = int(data["bandwidth"]) + if "bond" in data and data["bond"]: + create_param['port']['bond'] = int(data["bond"]) + if "macbond" in data and data["macbond"]: + if 'mac_address' in create_param['port']: + create_param['port']['mac_address'] += (',' + data["macbond"]) + else: + create_param['port']['mac_address'] = data["macbond"] + if "ip" in data and data["ip"]: + if subnet_id: + create_param['port']['fixed_ips'] = [{"subnet_id": subnet_id, "ip_address": data["ip"]}] + + if "allowed_address_pairs" in data and data["allowed_address_pairs"]: + create_param['port']['allowed_address_pairs'] = data["allowed_address_pairs"] + logger.info("[%s]call neutron.create_port(%s)" % (fun_name(), str(create_param))) + port_created = None + try: + port_created = neutron.create_port(create_param) + except NeutronClientException as ex: + logger.info("create_port exception: %s, %s", str(sys.exc_info()), ex.message) + create_param['port'].pop('security_groups') + if 'allowed_address_pairs' in create_param['port']: + create_param['port'].pop('allowed_address_pairs') + logger.info("[%s]recall neutron.create_port(%s)" % (fun_name(), str(create_param))) + port_created = neutron.create_port(create_param) + if port_created: + port_data = {'status': port_created['port']['status'], + 'id': port_created['port']['id'], + 'name': port_created['port']['name'], + 'network_id': port_created['port']['network_id'], + const.RES_TYPE_KEY: const.RES_TYPE_NEW} + return [0, port_data] + + +def create_network(auth_info, data): + neutron = neutronbase.get_neutron(fun_name(), auth_info, data["tenant"]) + tenant_id = project.get_tenant_id(fun_name(), auth_info, data["tenant"]) + + neutron_admin = neutronbase.get_neutron_default(fun_name(), auth_info) + all_nets = neutron_admin.list_networks() + filter_nets = [net for net in all_nets['networks'] if net['name'] == data["network_name"]] + sel_nets = [net for net in filter_nets if net['tenant_id'] == tenant_id or + (net['tenant_id'] != tenant_id and net['shared'])] + count_sel_nets = len(sel_nets) + if 1 < count_sel_nets: + for sel_net in sel_nets: + logger.info("net_id=%s, net_tenant_id=%s", sel_net["id"], sel_net['tenant_id']) + raise VimException("Already %d networks are found with name %s" % (count_sel_nets, data["network_name"])) + + network_data = None + if sel_nets: + if sel_nets[0]['tenant_id'] != tenant_id: + neutron = neutronbase.get_neutron_by_tenant_id(fun_name(), auth_info, sel_nets[0]['tenant_id']) + all_subnets = neutron_admin.list_subnets() + filter_subnets = [subnet for subnet in all_subnets["subnets"] if subnet["network_id"] == sel_nets[0]["id"]] + network_data = {"status": sel_nets[0]["status"], + "id": sel_nets[0]["id"], + "name": data["network_name"], + "provider:segmentation_id": sel_nets[0]["provider:segmentation_id"], + "provider:network_type": sel_nets[0]["provider:network_type"], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST, + "subnet_list": [{ + "id": subnet["id"], + "name": subnet["name"], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST + } for subnet in filter_subnets]} + else: + create_params = { + 'network': { + 'name': data["network_name"], + 'admin_state_up': True, + 'tenant_id': tenant_id, + 'shared': "shared" in data and int(data["shared"]) == const.SHARED_NET}} + if "mtu" in data and int(data["mtu"]) != const.DEFAULT_MTU: + create_params['network']['mtu'] = int(data["mtu"]) + if "vlan_transparent" in data and int(data["vlan_transparent"]) == const.SUPPORT_VLAN_TRANSPARENT: + create_params['network']['vlan-transparent'] = True + if "network_type" in data and data['network_type']: + create_params['network']['provider:network_type'] = data['network_type'] + if "segmentation_id" in data and data['segmentation_id']: + create_params['network']['provider:segmentation_id'] = int(data['segmentation_id']) + if "physical_network" in data and data['physical_network']: + create_params['network']['provider:physical_network'] = data['physical_network'] + + logger.info("[%s]call neutron.create_network(%s)" % (fun_name(), str(create_params))) + network_created = neutron.create_network(create_params) + network_data = {"status": network_created['network']['status'], + "id": network_created['network']['id'], + "name": data["network_name"], + "provider:segmentation_id": network_created['network']['provider:segmentation_id'], + "provider:network_type": network_created['network']['provider:network_type'], + const.RES_TYPE_KEY: const.RES_TYPE_NEW, + "subnet_list": []} + + # subnet create + exist_subnet_names = [subnet["name"] for subnet in network_data["subnet_list"]] + need_rollback, ret_error = False, None + if "subnet_list" in data and data["subnet_list"]: + for subnet_data in data["subnet_list"]: + if subnet_data["subnet_name"] in exist_subnet_names: + continue + ret = create_subnet(neutron, network_data["id"], subnet_data) + if ret[0] != 0: + need_rollback, ret_error = True, ret + break + network_data["subnet_list"].append(ret[1]) + + # rollback when failed to create subnet + if need_rollback: + rollback(neutron_admin, network_data) + return ret_error + + return [0, network_data] + + +def create_subnet(neutron, network_id, data): + all_subnets = neutron.list_subnets() + filter_subnets = [subnet for subnet in all_subnets["subnets"] + if subnet["network_id"] == network_id and subnet["name"] == data["subnet_name"]] + if filter_subnets: + return [0, { + "id": filter_subnets[0]["id"], + "name": data["subnet_name"], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST}] + try: + create_params = { + 'subnet': { + 'network_id': network_id, + 'name': data["subnet_name"], + 'cidr': data["cidr"], + 'ip_version': int(data["ip_version"]) if "ip_version" in data else const.IPV4, }} + create_params["subnet"]["enable_dhcp"] = ("enable_dhcp" in data + and int(data["enable_dhcp"]) == const.ENABLE_DHCP) + if "gateway_ip" in data and data["gateway_ip"]: + create_params["subnet"]["gateway_ip"] = data["gateway_ip"] + else: + create_params["subnet"]["gateway_ip"] = None + if "dns_nameservers" in data and data["dns_nameservers"]: + create_params["subnet"]["dns_nameservers"] = data["dns_nameservers"] + if "allocation_pools" in data and data["allocation_pools"]: + create_params["subnet"]["allocation_pools"] = data["allocation_pools"] + if "host_routes" in data and data["host_routes"]: + create_params["subnet"]["host_routes"] = data["host_routes"] + + logger.info("[%s]call neutron.create_subnet(%s)" % (fun_name(), str(create_params))) + subnet_created = neutron.create_subnet(create_params) + return [0, {"id": subnet_created["subnet"]["id"], + "name": data["subnet_name"], + const.RES_TYPE_KEY: const.RES_TYPE_NEW}] + except Exception as ex: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + return [1, ex.message if ex.message else str(sys.exc_info())] + + +def rollback(neutron, network_data): + for subnet_data in network_data["subnet_list"]: + if subnet_data[const.RES_TYPE_KEY] == const.RES_TYPE_NEW: + try: + logger.info("[%s]call neutron.delete_subnet(%s)" % (fun_name(), subnet_data["id"])) + neutron.delete_subnet(subnet_data["subnet_id"]) + except: + logger.error("[%s]%s", fun_name(), str(sys.exc_info())) + + if network_data and network_data[const.RES_TYPE_KEY] == const.RES_TYPE_NEW: + try: + logger.info("[%s]call neutron.delete_network(%s)" % (fun_name(), network_data["id"])) + neutron.delete_network(network_data["id"]) + except: + logger.error("[%s]%s", fun_name(), str(sys.exc_info())) + + +def delete_network(auth_info, network_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + try: + neutron.delete_network(network_id) + except Exception as ex: + logger.error(traceback.format_exc()) + msg = ex.message if ex.message else str(sys.exc_info()) + logger.error(msg) + if 404 == ex.status_code: + return [0, ex.message] + return [1, "Vim exception."] + return [0, "Network(%s) is deleted" % network_id] + + +def delete_subnet(auth_info, subnet_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + try: + neutron.delete_subnet(subnet_id) + except NeutronClientException as e: + logger.warn("[%s]NetworkNotFoundClient: %s", fun_name(), e.message) + return [0, e.message] + return [0, "Subnet(%s) is deleted" % subnet_id] + + +def delete_port(auth_info, port_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + try: + neutron.delete_port(port_id) + except NeutronClientException as e: + logger.warn("[%s]NeutronClientException: %s", fun_name(), e.message) + return [0, e.message] + return [0, "Port(%s) is deleted" % port_id] diff --git a/lcm/pub/nfvi/vim/api/openstack/neutronbase.py b/lcm/pub/nfvi/vim/api/openstack/neutronbase.py new file mode 100644 index 00000000..1a755cc8 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/neutronbase.py @@ -0,0 +1,46 @@ +# Copyright 2016 ZTE Corporation. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +import neutronclient.v2_0.client as neutronclient + +logger = logging.getLogger(__name__) + + +def get_neutron(funname, auth_info, tenant_name): + username = auth_info["user"] + passwd = auth_info["passwd"] + url = auth_info["url"] + cacert = auth_info["cacert"] + insecure = auth_info["insecure"] + logger.info("[%s]call neutronclient.Client(auth_url='%s'," + "username='%s',password='%s',tenant_name='%s',insecure=%s,ca_cert='%s')" + % (funname, url, username, passwd, tenant_name, insecure, cacert)) + return neutronclient.Client(username=username, password=passwd, tenant_name=tenant_name, + insecure=insecure, auth_url=url, ca_cert=cacert) + + +def get_neutron_by_tenant_id(funname, auth_info, tenant_id): + username = auth_info["user"] + passwd = auth_info["passwd"] + url = auth_info["url"] + cacert = auth_info["cacert"] + logger.info("[%s]call neutronclient.Client(auth_url='%s'," + "username='%s',password='%s',tenant_id='%s',ca_cert='%s')" + % (funname, url, username, passwd, tenant_id, cacert)) + return neutronclient.Client(username=username, password=passwd, tenant_id=tenant_id, auth_url=url, ca_cert=cacert) + + +def get_neutron_default(funname, auth_info): + return get_neutron(funname, auth_info, auth_info["tenant"]) diff --git a/lcm/pub/nfvi/vim/api/openstack/project.py b/lcm/pub/nfvi/vim/api/openstack/project.py new file mode 100644 index 00000000..b0c079ad --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/project.py @@ -0,0 +1,27 @@ +# Copyright 2016 ZTE Corporation. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from lcm.pub.nfvi.vim.lib.vimexception import VimException + + +logger = logging.getLogger(__name__) + + +def get_tenant_id(funname, auth_info, tenant): + logger.debug("[%s]call get_tenant_id(%s)", funname, tenant) + keystone = auth_info["keystone"] + tids = [t.id for t in keystone.tenants.list() if t.name == tenant] + if not tids: + raise VimException("Tenant(%s) does not exist." % tenant) + logger.debug("[%s]tenant_id=%s", funname, tids[0]) + return tids[0] diff --git a/lcm/pub/nfvi/vim/const.py b/lcm/pub/nfvi/vim/const.py new file mode 100644 index 00000000..e8798689 --- /dev/null +++ b/lcm/pub/nfvi/vim/const.py @@ -0,0 +1,26 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +VIM_OPENSTACK = "openstack" +VIM_VMWARE = "vmware" +RES_TYPE_KEY = "res_type" +RES_TYPE_NEW = 1 +RES_TYPE_EXIST = 0 +SHARED_NET = 1 +SUPPORT_VLAN_TRANSPARENT = 1 +DEFAULT_MTU = 1500 +IPV4 = 4 +IPV6 = 6 +ENABLE_DHCP = 1 diff --git a/lcm/pub/nfvi/vim/lib/__init__.py b/lcm/pub/nfvi/vim/lib/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/lib/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lcm/pub/nfvi/vim/lib/syscomm.py b/lcm/pub/nfvi/vim/lib/syscomm.py new file mode 100644 index 00000000..447bf4ee --- /dev/null +++ b/lcm/pub/nfvi/vim/lib/syscomm.py @@ -0,0 +1,20 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import inspect + + +def fun_name(): + return inspect.stack()[1][3] diff --git a/lcm/pub/nfvi/vim/lib/vimexception.py b/lcm/pub/nfvi/vim/lib/vimexception.py new file mode 100644 index 00000000..b2b09cc3 --- /dev/null +++ b/lcm/pub/nfvi/vim/lib/vimexception.py @@ -0,0 +1,17 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class VimException(Exception): + pass diff --git a/lcm/pub/nfvi/vim/test/__init__.py b/lcm/pub/nfvi/vim/test/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/test/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lcm/pub/nfvi/vim/test/openstack/__init__.py b/lcm/pub/nfvi/vim/test/openstack/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lcm/pub/nfvi/vim/test/openstack/pub.py b/lcm/pub/nfvi/vim/test/openstack/pub.py new file mode 100644 index 00000000..6c36f43e --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/pub.py @@ -0,0 +1,23 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from lcm.pub.nfvi.vim import const + +connect_info = { + "vimtype": const.VIM_OPENSTACK, + "url": "http://10.43.35.131:5000/v2.0", + "user": "admin", + "passwd": "keystone", + "tenant": "admin"} diff --git a/lcm/pub/nfvi/vim/test/openstack/test_image.py b/lcm/pub/nfvi/vim/test/openstack/test_image.py new file mode 100644 index 00000000..004d737b --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/test_image.py @@ -0,0 +1,91 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +import unittest +import os +import time + +from lcm.pub.nfvi.vim import vimadaptor +from lcm.pub.nfvi.vim.test.openstack import pub +from lcm.pub.nfvi.vim import const + + +class TestImage(unittest.TestCase): + def setUp(self): + self.api = vimadaptor.VimAdaptor(pub.connect_info) + self.currentdir = os.path.dirname(os.path.abspath(__file__)) + def tearDown(self): + pass + def createImg(self, data): + return self.api.create_image(data) + + def test_image_all(self): + image_data = { + "image_name": "cirros", + "image_path": self.currentdir + "/testdata/cirros.qcow2", + "image_type": "qcow2" + } + + # create image + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + if ret[1][const.RES_TYPE_KEY] == const.RES_TYPE_EXIST: + self.api.delete_image(image_id = ret[1]["id"]) + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + imageid = ret[1]["id"] + retryTimes = 0 + while retryTimes < 10: + ret = self.api.get_image(image_id = imageid) + self.assertEqual(0, ret[0]) + if ret[1]["status"] == 'active': + break + time.sleep(2) + + # image is exist + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + self.assertEqual(const.RES_TYPE_EXIST, ret[1][const.RES_TYPE_KEY]) + + # get all images + ret = self.api.get_images() + self.assertEqual(0, ret[0]) + flag = False + for image in ret[1]['image_list']: + if image_data["image_name"] == image["name"]: + flag = True + break + self.assertTrue(flag) + + # delete image + ret = self.api.delete_image(image_id = imageid) + self.assertEqual(0, ret[0]) + + # get_image except + ret = self.api.get_image(image_id = imageid) + self.assertEqual(2, ret[0]) + + # delete image except + ret = self.api.delete_image(image_id = imageid) + self.assertEqual(0, ret[0]) + + # Exception + image_data["image_path"] = "aaaa" + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + + imageid = ret[1]["id"] + self.api.delete_image(image_id = imageid) +""" diff --git a/lcm/pub/nfvi/vim/test/openstack/test_network.py b/lcm/pub/nfvi/vim/test/openstack/test_network.py new file mode 100644 index 00000000..54ed68d6 --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/test_network.py @@ -0,0 +1,245 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +import unittest +from lcm.pub.nfvi.vim import vimadaptor +from lcm.pub.nfvi.vim.api.openstack import neutronbase +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim.test.openstack import pub +from lcm.pub.nfvi.vim import const + +class TestNetwork(unittest.TestCase): + def setUp(self): + self.api = vimadaptor.VimAdaptor(pub.connect_info) + self.network_data = { + "tenant": "admin", + "network_name": "testnet1", + "shared": const.SHARED_NET, + "network_type": "", + "mtu": 1523, + "subnet_list": [{ + "subnet_name": "subnet1", + "cidr": "192.168.1.0/24", + "ip_version": const.IPV4, + "enable_dhcp": 0, + "gateway_ip": "192.168.1.1", + "dns_nameservers": [], + "allocation_pools":[], + "host_routes": [] + }] + } + + self.port_data = { + "port_name":"port_test", + "tenant_name":"test", + "network_name":"testnet1", + "mac_address":"fa:16:3e:c9:cb:f5", + "vnic_type":"normal", + "bandwidth":"100", + "bond":"0", + "macbond":"", + "ip":"192.168.1.10", + "subnet_name":"subnet1", + "allowed_address_pairs":[{'ip_address':'192.168.1.11','mac_address':'fa:16:3e:c9:cb:f6'}] + } + + self.port_data_no_subname = { + "port_name":"port_test_no_subname", + "tenant_name":"admin", + "network_name":"testnet1", + "mac_address":"fa:16:3e:c9:cb:f7", + "vnic_type":"normal", + "bandwidth":"100", + "bond":"0", + "macbond":"", + "ip":"192.168.1.12", + "allowed_address_pairs":[{'ip_address':'192.168.1.13','mac_address':'fa:16:3e:c9:cb:f8'}] + } + + self.network_data_rollback = { + "tenant": "admin", + "network_name": "testnet1", + "shared": const.SHARED_NET, + "network_type": "", + "mtu": 1523, + "subnet_list": [{ + "subnet_name": "subnet1", + "cidr": "192.168.1.0/24", + "ip_version": const.IPV4, + "enable_dhcp": 0, + "gateway_ip": "192.168.1.1", + "dns_nameservers": [], + "allocation_pools":[], + "host_routes": [] + }, + { + "subnet_name": "subnet2", + "cidr": "191.168.1.0/24", + "ip_version": const.IPV6, + "enable_dhcp": 0, + "gateway_ip": "191.168.1.1", + "dns_nameservers": [], + "allocation_pools":[], + "host_routes": [] + }] + } + def tearDown(self): + pass + + def test_network_all(self): + neutron = neutronbase.get_neutron_default(fun_name(), pub.connect_info) + + # create network + ret = self.api.create_network(self.network_data) + self.assertEqual(0, ret[0], ret[1]) + if ret[1][const.RES_TYPE_KEY] == const.RES_TYPE_EXIST: + for subnet in ret[1]["subnet_list"]: + ports = neutron.list_ports() + for port in ports['ports']: + for fixed_ip in port['fixed_ips']: + if fixed_ip['subnet_id'] == subnet["id"]: + self.api.delete_port(port['id']) + break + ret_del = self.api.delete_subnet(subnet_id = subnet["id"]) + self.assertEqual(0, ret_del[0]) + ret_del = self.api.delete_network(network_id = ret[1]["id"]) + self.assertEqual(0, ret_del[0]) + ret = self.api.create_network(self.network_data) + self.assertEqual(0, ret[0]) + + # network exist + ret = self.api.create_network(self.network_data) + self.assertEqual(0, ret[0]) + self.assertEqual(ret[1][const.RES_TYPE_KEY], const.RES_TYPE_EXIST) + + # query subnet + q_subnet_ret = self.api.query_subnet(ret[1]["subnet_list"][0]["id"]) + self.assertEqual(0, q_subnet_ret[0]) + + # query net + q_net_ret = self.api.query_net(ret[1]["id"]) + self.assertEqual(0, q_net_ret[0]) + + # query nets + q_nets_ret = self.api.query_nets() + self.assertEqual(0, q_nets_ret[0]) + flag = False + for network in q_nets_ret[1]['networks']: + if ret[1]["id"] == network["id"]: + flag = True + break + self.assertTrue(flag) + + # create port + create_port_ret = self.api.create_port(self.port_data) + self.assertEqual(0, create_port_ret[0], create_port_ret[1]) + + # port exist + create_port_ret = self.api.create_port(self.port_data) + self.assertEqual(0, create_port_ret[0]) + self.assertEqual(create_port_ret[1][const.RES_TYPE_KEY], const.RES_TYPE_EXIST) + + # create port no subname + ret_no_subname = self.api.create_port(self.port_data_no_subname) + self.assertEqual(0, ret_no_subname[0], ret_no_subname[1]) + self.api.delete_port(ret_no_subname[1]['id']) + + # create port except, networks not exist + network_name = self.port_data["network_name"] + self.port_data["network_name"] = "no_network" + create_port_except_ret = self.api.create_port(self.port_data) + self.assertEqual(1, create_port_except_ret[0]) + self.port_data["network_name"] = network_name + + # create port except, subnet not exist + subnet_name = self.port_data["subnet_name"] + self.port_data["subnet_name"] = "no_subnet" + create_port_except_ret = self.api.create_port(self.port_data) + self.assertEqual(1, create_port_except_ret[0]) + self.port_data["subnet_name"] = subnet_name + + # query port + q_port_ret = self.api.query_port(create_port_ret[1]['id']) + self.assertEqual(0, q_port_ret[0], q_port_ret[1]) + + # delete port + ret_port_del = self.api.delete_port(create_port_ret[1]['id']) + self.assertEqual(0, ret_port_del[0]) + + # delete network + for subnet in ret[1]["subnet_list"]: + ret_del = self.api.delete_subnet(subnet_id = subnet["id"]) + self.assertEqual(0, ret_del[0]) + ret_del = self.api.delete_network(network_id = ret[1]["id"]) + self.assertEqual(0, ret_del[0]) + + # query net except + q_net_ret = self.api.query_net(ret[1]["id"]) + self.assertEqual(2, q_net_ret[0]) + + # query subnet except + q_subnet_ret = self.api.query_subnet(ret[1]["subnet_list"][0]["id"]) + self.assertEqual(2, q_subnet_ret[0]) + + # rollback test + ret_roolback = self.api.create_network(self.network_data_rollback) + self.assertEqual(1, ret_roolback[0]) + + # delete except + ret_del = self.api.delete_subnet(subnet_id = "11111") + self.assertEqual(0, ret_del[0]) + + ret_del = self.api.delete_network(network_id = "11111") + self.assertEqual(0, ret_del[0]) + + ret_del = self.api.delete_port(port_id = "11111") + self.assertEqual(0, ret_del[0]) + + # query except + q_del = self.api.query_port(port_id = "11111") + self.assertEqual(2, q_del[0]) + + # multiple network except + tenant = self.network_data["tenant"] + network = self.network_data["network_name"] + self.network_data["tenant"] = "test" + self.network_data["network_name"] = "ut_test_network" + ret = self.api.create_network(self.network_data) + self.network_data["tenant"] = tenant + self.network_data["network_name"] = network + self.assertEqual(1, ret[0]) + + tenant = self.port_data["tenant_name"] + network = self.port_data["network_name"] + self.port_data["tenant_name"] = "test" + self.port_data["network_name"] = "ut_test_network" + ret = self.api.create_port(self.port_data) + self.port_data["tenant_name"] = tenant + self.port_data["network_name"] = network + self.assertEqual(1, ret[0]) + + # multiple subnet except + tenant = self.port_data["tenant_name"] + network = self.port_data["network_name"] + subnet = self.port_data["subnet_name"] + self.port_data["tenant_name"] = "test" + self.port_data["network_name"] = "ut_test_subnet_except" + self.port_data["subnet_name"] = "ut_test_same_subnet" + ret = self.api.create_port(self.port_data) + self.port_data["tenant_name"] = tenant + self.port_data["network_name"] = network + self.port_data["subnet_name"] = subnet + self.assertEqual(1, ret[0]) +""" diff --git a/lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2 b/lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2 Binary files differnew file mode 100644 index 00000000..afe3ca37 --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2 diff --git a/lcm/pub/nfvi/vim/vimadaptor.py b/lcm/pub/nfvi/vim/vimadaptor.py new file mode 100644 index 00000000..5f08d0b1 --- /dev/null +++ b/lcm/pub/nfvi/vim/vimadaptor.py @@ -0,0 +1,120 @@ +# Copyright 2016 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import sys +import traceback + +from requests import RequestException + +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim import const +from lcm.pub.nfvi.vim.lib.vimexception import VimException + +logger = logging.getLogger(__name__) + + +class VimAdaptor: + def __init__(self, connectInfo): + logger.info("[VimAdaptor]connectInfo=%s" % connectInfo) + self.apiImpl, self.authInfo = None, [1, "No auth info"] + self.create_api(connectInfo) + self.force_login(connectInfo) + + def create_api(self, connectInfo): + vimtype = connectInfo['vimtype'] if 'vimtype' in connectInfo else None + logger.info("call %s, vimtype=%s" % (fun_name(), vimtype)) + if vimtype == const.VIM_OPENSTACK: + from lcm.pub.nfvi.vim.api.openstack.api import OpenstackApi + self.apiImpl = OpenstackApi() + elif vimtype == const.VIM_VMWARE: + from lcm.pub.nfvi.vim.api.multivim.api import MultiVimApi + self.apiImpl = MultiVimApi() + else: + self.authInfo = [1, "Unsupported vimtype(%s)" % vimtype] + + def api_call(self, funname, fun, *args): + logger.info("call %s%s" % (funname, str(args))) + ret = None + try: + ret = fun(self.authInfo[1], *args) if self.authInfo[0] == 0 else self.authInfo + except VimException as e: + ret = [1, e.message] + except RequestException as e: + logger.error("request=%s, url=%s" % (e.request.headers._store, e.request.url)) + logger.error(traceback.format_exc()) + ret = [1, e.message if e.message else str(sys.exc_info())] + except Exception as ex: + logger.error(traceback.format_exc()) + ret = [1, ex.message if ex.message else str(sys.exc_info())] + except: + logger.error(traceback.format_exc()) + ret = [1, str(sys.exc_info())] + logger.info("[%s]ret=%s" % (funname, ret)) + return ret + + def force_login(self, connectInfo): + if self.apiImpl: + logger.info("call %s(%s)" % (fun_name(), connectInfo)) + try: + self.authInfo = self.apiImpl.login(connectInfo) + except VimException as e: + self.authInfo = [1, e.message] + except Exception as ex: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + self.authInfo = [1, ex.message if ex.message else str(sys.exc_info())] + except: + logger.error(traceback.format_exc()) + self.authInfo = [1, str(sys.exc_info())] + logger.info("self.authInfo=%s" % self.authInfo) + + def query_net(self, net_id): + return self.api_call(fun_name(), self.apiImpl.query_net, net_id) + + def query_nets(self): + return self.api_call(fun_name(), self.apiImpl.query_nets) + + def query_subnet(self, subnet_id): + return self.api_call(fun_name(), self.apiImpl.query_subnet, subnet_id) + + def query_port(self, port_id): + return self.api_call(fun_name(), self.apiImpl.query_port, port_id) + + def create_image(self, data): + return self.api_call(fun_name(), self.apiImpl.create_image, data) + + def get_image(self, image_id): + return self.api_call(fun_name(), self.apiImpl.get_image, image_id) + + def get_images(self): + return self.api_call(fun_name(), self.apiImpl.get_images) + + def delete_image(self, image_id): + return self.api_call(fun_name(), self.apiImpl.delete_image, image_id) + + def create_network(self, data): + return self.api_call(fun_name(), self.apiImpl.create_network, data) + + def delete_network(self, network_id): + return self.api_call(fun_name(), self.apiImpl.delete_network, network_id) + + def delete_subnet(self, subnet_id): + return self.api_call(fun_name(), self.apiImpl.delete_subnet, subnet_id) + + def create_port(self, data): + return self.api_call(fun_name(), self.apiImpl.create_port, data) + + def delete_port(self, port_id): + return self.api_call(fun_name(), self.apiImpl.delete_port, port_id) |