417 lines
15 KiB
Python
417 lines
15 KiB
Python
# Copyright 2012 Nicira Networks, Inc.
|
|
#
|
|
# 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.
|
|
#
|
|
# @author: Brad Hall, Nicira Networks, Inc.
|
|
|
|
import logging
|
|
|
|
from quantum.common import exceptions as exception
|
|
from quantum.openstack.common import jsonutils
|
|
from quantum.plugins.nicira.nicira_nvp_plugin import NvpApiClient
|
|
|
|
|
|
LOG = logging.getLogger("nvplib")
|
|
LOG.setLevel(logging.INFO)
|
|
|
|
|
|
def do_single_request(*args, **kwargs):
|
|
"""Issue a request to a specified controller if specified via kwargs
|
|
(controller=<controller>)."""
|
|
controller = kwargs["controller"]
|
|
LOG.debug("Issuing request to controller: %s" % controller.name)
|
|
return controller.api_client.request(*args)
|
|
|
|
|
|
def check_default_transport_zone(c):
|
|
"""Make sure the default transport zone specified in the config exists"""
|
|
msg = []
|
|
# This will throw an exception on failure and that's ok since it will
|
|
# just propogate to the cli.
|
|
resp = do_single_request(
|
|
"GET",
|
|
"/ws.v1/transport-zone?uuid=%s" % c.default_tz_uuid,
|
|
controller=c)
|
|
result = jsonutils.loads(resp)
|
|
if int(result["result_count"]) == 0:
|
|
msg.append("Unable to find zone \"%s\" for controller \"%s\"" %
|
|
(c.default_tz_uuid, c.name))
|
|
if len(msg) > 0:
|
|
raise Exception(' '.join(msg))
|
|
|
|
|
|
def check_tenant(controller, net_id, tenant_id):
|
|
"""Return true if the tenant "owns" this network"""
|
|
net = get_network(controller, net_id)
|
|
for t in net["tags"]:
|
|
if t["scope"] == "os_tid" and t["tag"] == tenant_id:
|
|
return True
|
|
return False
|
|
|
|
# -------------------------------------------------------------------
|
|
# Network functions
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
def get_network(controller, net_id):
|
|
path = "/ws.v1/lswitch/%s" % net_id
|
|
try:
|
|
resp_obj = do_single_request("GET", path, controller=controller)
|
|
network = jsonutils.loads(resp_obj)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
raise exception.NetworkNotFound(net_id=net_id)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
LOG.debug("Got network \"%s\": %s" % (net_id, network))
|
|
return network
|
|
|
|
|
|
def create_lswitch(controller, lswitch_obj):
|
|
LOG.debug("Creating lswitch: %s" % lswitch_obj)
|
|
# Warn if no tenant is specified
|
|
found = "os_tid" in [x["scope"] for x in lswitch_obj["tags"]]
|
|
if not found:
|
|
LOG.warn("No tenant-id tag specified in logical switch: %s" %
|
|
lswitch_obj)
|
|
uri = "/ws.v1/lswitch"
|
|
try:
|
|
resp_obj = do_single_request("POST", uri,
|
|
jsonutils.dumps(lswitch_obj),
|
|
controller=controller)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
|
|
r = jsonutils.loads(resp_obj)
|
|
d = {}
|
|
d["net-id"] = r["uuid"]
|
|
d["net-name"] = r["display_name"]
|
|
LOG.debug("Created logical switch: %s" % d["net-id"])
|
|
return d
|
|
|
|
|
|
def update_network(controller, network, **kwargs):
|
|
uri = "/ws.v1/lswitch/" + network
|
|
lswitch_obj = {}
|
|
if "name" in kwargs:
|
|
lswitch_obj["display_name"] = kwargs["name"]
|
|
try:
|
|
resp_obj = do_single_request("PUT",
|
|
uri,
|
|
jsonutils.dumps(lswitch_obj),
|
|
controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Network not found, Error: %s" % str(e))
|
|
raise exception.NetworkNotFound(net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
|
|
obj = jsonutils.loads(resp_obj)
|
|
return obj
|
|
|
|
|
|
def get_all_networks(controller, tenant_id, networks):
|
|
"""Append the quantum network uuids we can find in the given controller to
|
|
"networks"
|
|
"""
|
|
uri = "/ws.v1/lswitch?fields=*&tag=%s&tag_scope=os_tid" % tenant_id
|
|
try:
|
|
resp_obj = do_single_request("GET", uri, controller=controller)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
if not resp_obj:
|
|
return []
|
|
lswitches = jsonutils.loads(resp_obj)["results"]
|
|
for lswitch in lswitches:
|
|
net_id = lswitch["uuid"]
|
|
if net_id not in [x["net-id"] for x in networks]:
|
|
networks.append({"net-id": net_id,
|
|
"net-name": lswitch["display_name"]})
|
|
return networks
|
|
|
|
|
|
def query_networks(controller, tenant_id, fields="*", tags=None):
|
|
uri = "/ws.v1/lswitch?fields=%s" % fields
|
|
if tags:
|
|
for t in tags:
|
|
uri += "&tag=%s&tag_scope=%s" % (t[0], t[1])
|
|
try:
|
|
resp_obj = do_single_request("GET", uri, controller=controller)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
if not resp_obj:
|
|
return []
|
|
lswitches = jsonutils.loads(resp_obj)["results"]
|
|
nets = [{'net-id': lswitch["uuid"],
|
|
'net-name': lswitch["display_name"]}
|
|
for lswitch in lswitches]
|
|
return nets
|
|
|
|
|
|
def delete_network(controller, network):
|
|
delete_networks(controller, [network])
|
|
|
|
|
|
def delete_networks(controller, networks):
|
|
for network in networks:
|
|
path = "/ws.v1/lswitch/%s" % network
|
|
|
|
try:
|
|
do_single_request("DELETE", path, controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Network not found, Error: %s" % str(e))
|
|
raise exception.NetworkNotFound(net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
|
|
|
|
def create_network(tenant_id, net_name, **kwargs):
|
|
controller = kwargs["controller"]
|
|
|
|
transport_zone = kwargs.get("transport_zone",
|
|
controller.default_tz_uuid)
|
|
transport_type = kwargs.get("transport_type", "gre")
|
|
lswitch_obj = {
|
|
"display_name": net_name,
|
|
"transport_zones": [{
|
|
"zone_uuid": transport_zone,
|
|
"transport_type": transport_type,
|
|
}],
|
|
"tags": [{"tag": tenant_id, "scope": "os_tid"}],
|
|
}
|
|
|
|
net = create_lswitch(controller, lswitch_obj)
|
|
net['net-op-status'] = "UP"
|
|
return net
|
|
|
|
#---------------------------------------------------------------------
|
|
# Port functions
|
|
#---------------------------------------------------------------------
|
|
|
|
|
|
def get_port_stats(controller, network_id, port_id):
|
|
try:
|
|
do_single_request("GET", "/ws.v1/lswitch/%s" % (network_id),
|
|
controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Network not found, Error: %s" % str(e))
|
|
raise exception.NetworkNotFound(net_id=network_id)
|
|
try:
|
|
path = "/ws.v1/lswitch/%s/lport/%s/statistic" % (network_id, port_id)
|
|
resp = do_single_request("GET", path, controller=controller)
|
|
stats = jsonutils.loads(resp)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Port not found, Error: %s" % str(e))
|
|
raise exception.PortNotFound(port_id=port_id, net_id=network_id)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
LOG.debug("Returning stats for port \"%s\" on \"%s\": %s" % (port_id,
|
|
network_id,
|
|
stats))
|
|
return stats
|
|
|
|
|
|
def check_port_state(state):
|
|
if state not in ["ACTIVE", "DOWN"]:
|
|
LOG.error("Invalid port state (ACTIVE and DOWN are valid states): %s" %
|
|
state)
|
|
raise exception.StateInvalid(port_state=state)
|
|
|
|
|
|
def query_ports(controller, network, relations=None, fields="*", filters=None):
|
|
uri = "/ws.v1/lswitch/" + network + "/lport?"
|
|
if relations:
|
|
uri += "relations=%s" % relations
|
|
uri += "&fields=%s" % fields
|
|
if filters and "attachment" in filters:
|
|
uri += "&attachment_vif_uuid=%s" % filters["attachment"]
|
|
try:
|
|
resp_obj = do_single_request("GET", uri, controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Network not found, Error: %s" % str(e))
|
|
raise exception.NetworkNotFound(net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
return jsonutils.loads(resp_obj)["results"]
|
|
|
|
|
|
def delete_port(controller, network, port):
|
|
uri = "/ws.v1/lswitch/" + network + "/lport/" + port
|
|
try:
|
|
do_single_request("DELETE", uri, controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Port or Network not found, Error: %s" % str(e))
|
|
raise exception.PortNotFound(port_id=port, net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
|
|
|
|
def delete_all_ports(controller, ls_uuid):
|
|
res = do_single_request("GET", "/ws.v1/lswitch/%s/lport?fields=uuid" %
|
|
ls_uuid, controller=controller)
|
|
res = jsonutils.loads(res)
|
|
for r in res["results"]:
|
|
do_single_request(
|
|
"DELETE",
|
|
"/ws.v1/lswitch/%s/lport/%s" % (ls_uuid, r["uuid"]),
|
|
controller=controller)
|
|
|
|
|
|
def get_port(controller, network, port, relations=None):
|
|
uri = "/ws.v1/lswitch/" + network + "/lport/" + port + "?"
|
|
if relations:
|
|
uri += "relations=%s" % relations
|
|
try:
|
|
resp_obj = do_single_request("GET", uri, controller=controller)
|
|
port = jsonutils.loads(resp_obj)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Port or Network not found, Error: %s" % str(e))
|
|
raise exception.PortNotFound(port_id=port, net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
return port
|
|
|
|
|
|
def plug_interface(controller, network, port, type, attachment=None):
|
|
uri = "/ws.v1/lswitch/" + network + "/lport/" + port + "/attachment"
|
|
|
|
lport_obj = {}
|
|
if attachment:
|
|
lport_obj["vif_uuid"] = attachment
|
|
|
|
lport_obj["type"] = type
|
|
try:
|
|
resp_obj = do_single_request("PUT",
|
|
uri,
|
|
jsonutils.dumps(lport_obj),
|
|
controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Port or Network not found, Error: %s" % str(e))
|
|
raise exception.PortNotFound(port_id=port, net_id=network)
|
|
except NvpApiClient.Conflict as e:
|
|
LOG.error("Conflict while making attachment to port, "
|
|
"Error: %s" % str(e))
|
|
raise exception.AlreadyAttached(att_id=attachment,
|
|
port_id=port,
|
|
net_id=network,
|
|
att_port_id="UNKNOWN")
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
|
|
result = jsonutils.dumps(resp_obj)
|
|
return result
|
|
|
|
|
|
def unplug_interface(controller, network, port):
|
|
uri = "/ws.v1/lswitch/" + network + "/lport/" + port + "/attachment"
|
|
lport_obj = {"type": "NoAttachment"}
|
|
try:
|
|
resp_obj = do_single_request("PUT",
|
|
uri,
|
|
jsonutils.dumps(lport_obj),
|
|
controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Port or Network not found, Error: %s" % str(e))
|
|
raise exception.PortNotFound(port_id=port, net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
return jsonutils.loads(resp_obj)
|
|
|
|
|
|
def update_port(network, port_id, **params):
|
|
controller = params["controller"]
|
|
lport_obj = {}
|
|
|
|
if "state" in params:
|
|
state = params["state"]
|
|
check_port_state(state)
|
|
admin_status = True
|
|
if state == "DOWN":
|
|
admin_status = False
|
|
lport_obj["admin_status_enabled"] = admin_status
|
|
|
|
uri = "/ws.v1/lswitch/" + network + "/lport/" + port_id
|
|
try:
|
|
resp_obj = do_single_request("PUT",
|
|
uri,
|
|
jsonutils.dumps(lport_obj),
|
|
controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Port or Network not found, Error: %s" % str(e))
|
|
raise exception.PortNotFound(port_id=port_id, net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
|
|
obj = jsonutils.loads(resp_obj)
|
|
obj["port-op-status"] = get_port_status(controller, network, obj["uuid"])
|
|
return obj
|
|
|
|
|
|
def create_port(tenant, network, port_init_state, **params):
|
|
# Check initial state -- this throws an exception if the port state is
|
|
# invalid
|
|
check_port_state(port_init_state)
|
|
|
|
controller = params["controller"]
|
|
|
|
ls_uuid = network
|
|
|
|
admin_status = True
|
|
if port_init_state == "DOWN":
|
|
admin_status = False
|
|
lport_obj = {"admin_status_enabled": admin_status}
|
|
|
|
path = "/ws.v1/lswitch/" + ls_uuid + "/lport"
|
|
try:
|
|
resp_obj = do_single_request("POST",
|
|
path,
|
|
jsonutils.dumps(lport_obj),
|
|
controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Network not found, Error: %s" % str(e))
|
|
raise exception.NetworkNotFound(net_id=network)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
|
|
result = jsonutils.loads(resp_obj)
|
|
result['port-op-status'] = get_port_status(controller, ls_uuid,
|
|
result['uuid'])
|
|
return result
|
|
|
|
|
|
def get_port_status(controller, lswitch_id, port_id):
|
|
"""Retrieve the operational status of the port"""
|
|
# Make sure the network exists first
|
|
try:
|
|
do_single_request("GET", "/ws.v1/lswitch/%s" % (lswitch_id),
|
|
controller=controller)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Network not found, Error: %s" % str(e))
|
|
raise exception.NetworkNotFound(net_id=lswitch_id)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
try:
|
|
r = do_single_request(
|
|
"GET",
|
|
"/ws.v1/lswitch/%s/lport/%s/status" % (lswitch_id, port_id),
|
|
controller=controller)
|
|
r = jsonutils.loads(r)
|
|
except NvpApiClient.ResourceNotFound as e:
|
|
LOG.error("Port not found, Error: %s" % str(e))
|
|
raise exception.PortNotFound(port_id=port_id, net_id=lswitch_id)
|
|
except NvpApiClient.NvpApiException as e:
|
|
raise exception.QuantumException()
|
|
if r['link_status_up'] is True:
|
|
return "UP"
|
|
else:
|
|
return "DOWN"
|