Lisa Zangrando 91919336b8 Invalid input for field/attribute quota_class_set
The optional input attribute 'force' for os-quota-class-sets is no more
supported in Mitaka and so NovaManager throws the following exception
reported in the log file:

ERROR - error on updating the quota info (id=<built-in function id>):
{u'badRequest': {u'message': u"Invalid input for field/attribute
quota_class_set. Value: {u'cores': 2, u'ram': 1024, u'force': True,
u'instances': 10}. Additional properties are not allowed
(u'force' was unexpected)", u'code': 400}}

- fixed method: NovaManager.updateQuota()
- moved some not useful logging info from LOG.info() to LOG.debug()

Change-Id: I72a401beee24b24bd2f8fc6dc8a3784813af0864
Sem-Ver: bugfix
Closes-bug: #1645397
2016-11-28 17:39:28 +00:00

1259 lines
48 KiB
Python

import common.utils as utils
import ConfigParser
import eventlet
import hashlib
import hmac
import json
import logging
import oslo_messaging as oslo_msg
import requests
from common.block_device import BlockDeviceMapping
from common.compute import Compute
from common.flavor import Flavor
from common.hypervisor import Hypervisor
from common.quota import Quota
from common.request import Request
from common.server import Server
from nova.baserpc import BaseAPI
from nova.compute.rpcapi import ComputeAPI
from nova.conductor.rpcapi import ComputeTaskAPI
from nova.conductor.rpcapi import ConductorAPI
from nova.objects import base as objects_base
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_versionedobjects import base as ovo_base
from sqlalchemy import create_engine
from sqlalchemy.exc import SQLAlchemyError
from synergy.common.manager import Manager
__author__ = "Lisa Zangrando"
__email__ = "lisa.zangrando[AT]pd.infn.it"
__copyright__ = """Copyright (c) 2015 INFN - INDIGO-DataCloud
All Rights Reserved
Licensed under the Apache License, Version 2.0;
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."""
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONFIG = ConfigParser.SafeConfigParser()
class MessagingAPI(object):
def __init__(self, transport_url):
LOG.debug("setting up the AMQP transport url: %s" % transport_url)
oslo_msg.set_transport_defaults(control_exchange="nova")
self.TRANSPORT = oslo_msg.get_transport(CONF, url=transport_url)
def getTarget(self, topic, exchange=None, namespace=None,
version=None, server=None):
return oslo_msg.Target(topic=topic,
exchange=exchange,
namespace=namespace,
version=version,
server=server)
def getRPCClient(self, target, version_cap=None, serializer=None):
assert self.TRANSPORT is not None
LOG.debug("creating RPC client with target %s" % target)
return oslo_msg.RPCClient(self.TRANSPORT,
target,
version_cap=version_cap,
serializer=serializer)
def getRPCServer(self, target, endpoints, serializer=None):
assert self.TRANSPORT is not None
LOG.debug("creating RPC server with target %s" % target)
return oslo_msg.get_rpc_server(self.TRANSPORT,
target,
endpoints,
executor="eventlet",
serializer=serializer)
def getNotificationListener(self, targets, endpoints):
assert self.TRANSPORT is not None
LOG.debug("creating notification listener with target %s endpoints %s"
% (targets, endpoints))
return oslo_msg.get_notification_listener(self.TRANSPORT,
targets,
endpoints,
allow_requeue=True,
executor="eventlet")
class NovaBaseRPCAPI(BaseAPI):
def __init__(self, topic, msg):
self.target = msg.getTarget(topic=None,
namespace="baseapi",
version="1.1")
target_synergy = msg.getTarget(topic=topic + "_synergy",
namespace="baseapi",
version="1.0")
self.client = msg.getRPCClient(target=target_synergy, version_cap=None)
def ping(self, context, arg, timeout=None):
try:
cctxt = self.client.prepare(timeout=timeout)
return cctxt.call(context, 'ping', arg=arg)
except Exception as ex:
LOG.error("NovaBaseRPCAPI ping! %s" % (ex))
raise ex
def get_backdoor_port(self, context, host):
cctxt = self.client.prepare(server=host, version='1.1')
return cctxt.call(context, 'get_backdoor_port')
class NovaComputeAPI(ComputeAPI):
def __init__(self, topic, msg):
self.target = msg.getTarget(topic=topic, version="4.0")
self.client = msg.getRPCClient(target=msg.getTarget(topic=topic))
def build_and_run_instance(self, context, instance, host, image,
request_spec, filter_properties,
admin_password=None, injected_files=None,
requested_networks=None, security_groups=None,
block_device_mapping=None, node=None,
limits=None):
if not filter_properties.get('force_hosts', None):
filter_properties['limits'] = limits
bdms = []
for block_device in block_device_mapping:
bdms.append(block_device.serialize())
version = '4.0'
cctxt = self.client.prepare(server=host, version=version)
cctxt.cast(context,
'build_and_run_instance',
instance=instance,
image=image,
request_spec=request_spec,
filter_properties=filter_properties,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=bdms,
node=node,
limits=limits)
class NovaConductorAPI(ConductorAPI):
def __init__(self, topic, msg):
report_interval = cfg.IntOpt("report_interval",
default=10,
help="Seconds between nodes reporting "
"state to datastore")
CONF.register_opt(report_interval)
self.target = msg.getTarget(topic=topic, version="3.0")
self.client = msg.getRPCClient(
target=msg.getTarget(topic=topic + "_synergy", version="3.0"),
version_cap=None)
def provider_fw_rule_get_all(self, context):
cctxt = self.client.prepare()
return cctxt.call(context, 'provider_fw_rule_get_all')
def object_class_action(self, context, objname, objmethod, objver,
args, kwargs):
versions = ovo_base.obj_tree_get_versions(objname)
return self.object_class_action_versions(context,
objname,
objmethod,
versions,
args, kwargs)
def object_class_action_versions(self, context, objname, objmethod,
object_versions, args, kwargs):
cctxt = self.client.prepare()
return cctxt.call(context, 'object_class_action_versions',
objname=objname, objmethod=objmethod,
object_versions=object_versions,
args=args, kwargs=kwargs)
def object_action(self, context, objinst, objmethod, args, kwargs):
cctxt = self.client.prepare()
return cctxt.call(context, 'object_action', objinst=objinst,
objmethod=objmethod, args=args, kwargs=kwargs)
def object_backport_versions(self, context, objinst, object_versions):
cctxt = self.client.prepare()
return cctxt.call(context, 'object_backport_versions', objinst=objinst,
object_versions=object_versions)
class NovaConductorComputeAPI(ComputeTaskAPI):
def __init__(self, topic, scheduler_manager, keystone_manager, msg):
self.topic = topic
self.scheduler_manager = scheduler_manager
self.keystone_manager = keystone_manager
self.messagingAPI = msg
serializer = objects_base.NovaObjectSerializer()
self.target = self.messagingAPI.getTarget(topic=topic,
namespace="compute_task",
version="1.11")
self.client = self.messagingAPI.getRPCClient(
target=oslo_msg.Target(topic=topic + "_synergy",
namespace="compute_task",
version="1.10"),
serializer=serializer)
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping=None,
legacy_bdm=True):
# token = self.keystone_manager.validateToken(context["auth_token"])
for instance in instances:
try:
request = Request.build(context, instance, image,
filter_properties, admin_password,
injected_files, requested_networks,
security_groups, block_device_mapping,
legacy_bdm)
self.scheduler_manager.processRequest(request)
except Exception as ex:
LOG.error("Exception has occured", exc_info=1)
LOG.error(ex)
def build_instance(self, context, instance, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping=None,
legacy_bdm=True):
try:
version = '1.10'
if not self.client.can_send_version(version):
version = '1.9'
if 'instance_type' in filter_properties:
flavor = filter_properties['instance_type']
flavor_p = objects_base.obj_to_primitive(flavor)
filter_properties = dict(filter_properties,
instance_type=flavor_p)
kw = {'instances': [instance],
'image': image,
'filter_properties': filter_properties,
'admin_password': admin_password,
'injected_files': injected_files,
'requested_networks': requested_networks,
'security_groups': security_groups}
if not self.client.can_send_version(version):
version = '1.8'
kw['requested_networks'] = kw['requested_networks'].as_tuples()
if not self.client.can_send_version('1.7'):
version = '1.5'
bdm_p = objects_base.obj_to_primitive(block_device_mapping)
kw.update({'block_device_mapping': bdm_p,
'legacy_bdm': legacy_bdm})
cctxt = self.client.prepare(version_cap=version)
cctxt.cast(context, 'build_instances', **kw)
except Exception as ex:
LOG.error("Exception has occured", exc_info=1)
LOG.error(ex)
raise ex
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit,
reservations=None, clean_shutdown=True,
request_spec=None):
kw = {'instance': instance, 'scheduler_hint': scheduler_hint,
'live': live, 'rebuild': rebuild, 'flavor': flavor,
'block_migration': block_migration,
'disk_over_commit': disk_over_commit,
'reservations': reservations,
'clean_shutdown': clean_shutdown,
'request_spec': request_spec,
}
version = '1.13'
if not self.client.can_send_version(version):
del kw['request_spec']
version = '1.11'
if not self.client.can_send_version(version):
del kw['clean_shutdown']
version = '1.10'
if not self.client.can_send_version(version):
kw['flavor'] = objects_base.obj_to_primitive(flavor)
version = '1.6'
if not self.client.can_send_version(version):
kw['instance'] = jsonutils.to_primitive(
objects_base.obj_to_primitive(instance))
version = '1.4'
cctxt = self.client.prepare(version=version)
return cctxt.call(context, 'migrate_server', **kw)
def unshelve_instance(self, context, instance):
cctxt = self.client.prepare(version='1.3')
cctxt.cast(context, 'unshelve_instance', instance=instance)
def rebuild_instance(self, ctxt, instance, new_pass, injected_files,
image_ref, orig_image_ref, orig_sys_metadata, bdms,
recreate=False, on_shared_storage=False, host=None,
preserve_ephemeral=False, kwargs=None):
cctxt = self.client.prepare(version='1.8')
cctxt.cast(ctxt, 'rebuild_instance',
instance=instance, new_pass=new_pass,
injected_files=injected_files, image_ref=image_ref,
orig_image_ref=orig_image_ref,
orig_sys_metadata=orig_sys_metadata, bdms=bdms,
recreate=recreate, on_shared_storage=on_shared_storage,
preserve_ephemeral=preserve_ephemeral,
host=host)
class NovaManager(Manager):
def __init__(self):
super(NovaManager, self).__init__("NovaManager")
self.config_opts = [
cfg.StrOpt("amqp_backend",
help="the amqp backend tpye (e.g. rabbit, qpid)",
default=None,
required=True),
cfg.StrOpt("amqp_host",
help="the amqp host name",
default="localhost",
required=False),
cfg.IntOpt("amqp_port",
help="the amqp listening port",
default=5672,
required=False),
cfg.StrOpt("amqp_user",
help="the amqp user",
default=None,
required=True),
cfg.StrOpt("amqp_password",
help="the amqp password",
default=None,
required=True),
cfg.StrOpt("amqp_virt_host",
help="the amqp virtual host",
default="/",
required=False),
cfg.StrOpt("conductor_topic",
help="the conductor topic",
default="conductor",
required=False),
cfg.StrOpt("compute_topic",
help="the compute topic",
default="compute",
required=False),
cfg.StrOpt("scheduler_topic",
help="the scheduler topic",
default="scheduler",
required=False),
cfg.StrOpt("metadata_proxy_shared_secret",
help="the metadata proxy shared secret",
default=None,
required=True),
cfg.FloatOpt("cpu_allocation_ratio",
help="the cpu allocation ratio",
default=float(16),
required=False),
cfg.FloatOpt("ram_allocation_ratio",
help="the ram allocation ratio",
default=float(1.5),
required=False),
cfg.StrOpt("db_connection",
help="the NOVA database connection",
default=None,
required=True),
cfg.StrOpt("host",
help="the host name",
default="localhost",
required=False),
cfg.IntOpt("timeout",
help="set the http connection timeout",
default=60,
required=False)
]
def setup(self):
eventlet.monkey_patch(os=False)
self.timeout = CONF.NovaManager.timeout
if self.getManager("KeystoneManager") is None:
raise Exception("KeystoneManager not found!")
if self.getManager("SchedulerManager") is None:
raise Exception("SchedulerManager not found!")
self.keystone_manager = self.getManager("KeystoneManager")
self.scheduler_manager = self.getManager("SchedulerManager")
amqp_backend = self.getParameter("amqp_backend", fallback=True)
amqp_host = self.getParameter("amqp_host")
amqp_port = self.getParameter("amqp_port")
amqp_user = self.getParameter("amqp_user", fallback=True)
amqp_password = self.getParameter("amqp_password", fallback=True)
amqp_virt_host = self.getParameter("amqp_virt_host")
db_connection = self.getParameter("db_connection", fallback=True)
host = self.getParameter("host")
conductor_topic = self.getParameter("conductor_topic")
compute_topic = self.getParameter("compute_topic")
self.getParameter("metadata_proxy_shared_secret", fallback=True)
try:
LOG.debug("setting up the NOVA database connection: %s"
% db_connection)
self.db_engine = create_engine(db_connection)
transport_url = "{b}://{user}:{password}@{host}:{port}/{virt_host}"
transport_url = transport_url.format(
b=amqp_backend,
user=amqp_user,
password=amqp_password,
host=amqp_host,
port=amqp_port,
virt_host=amqp_virt_host)
self.messagingAPI = MessagingAPI(transport_url)
self.novaBaseRPCAPI = NovaBaseRPCAPI(conductor_topic,
self.messagingAPI)
self.novaConductorAPI = NovaConductorAPI(conductor_topic,
self.messagingAPI)
self.novaConductorComputeAPI = NovaConductorComputeAPI(
conductor_topic,
self.scheduler_manager,
self.keystone_manager,
self.messagingAPI)
self.conductor_rpc = self.messagingAPI.getRPCServer(
target=self.messagingAPI.getTarget(topic=conductor_topic,
server=host),
endpoints=[self.novaBaseRPCAPI,
self.novaConductorAPI,
self.novaConductorComputeAPI])
self.conductor_rpc.start()
self.novaComputeAPI = NovaComputeAPI(compute_topic,
self.messagingAPI)
self.compute_rpc = self.messagingAPI.getRPCServer(
target=self.messagingAPI.getTarget(topic=compute_topic,
server=host),
endpoints=[self.novaComputeAPI])
except Exception as ex:
LOG.error("Exception has occured", exc_info=1)
LOG.error("NovaManager initialization failed! %s" % (ex))
raise ex
def execute(self, command, *args, **kargs):
raise Exception("command=%r not supported!" % command)
def task(self):
pass
def destroy(self):
pass
def getParameter(self, name, fallback=False):
result = CONF.NovaManager.get(name, None)
if result is not None:
return result
if fallback is True:
raise Exception("No attribute %r found in [NovaManager] "
"section of synergy.conf" % name)
else:
return None
def getUserData(self, server):
if not server:
return None
secret = CONF.NovaManager.metadata_proxy_shared_secret
if not secret:
return Exception("'metadata_proxy_shared_secret' "
"attribute not defined in synergy.conf")
digest = hmac.new(secret, server.getId(), hashlib.sha256).hexdigest()
self.keystone_manager.authenticate()
token = self.keystone_manager.getToken()
service = token.getService("nova")
if not service:
raise Exception("nova service not found!")
endpoint = service.getEndpoint("public")
if not endpoint:
raise Exception("nova endpoint not found!")
url = endpoint.getURL()
url = url[:url.rfind(":") + 1] + "8775/openstack/2015-10-15/user_data"
headers = {"Content-Type": "application/text",
"Accept": "application/text",
"User-Agent": "synergy",
"x-instance-id": server.getId(),
"x-tenant-id": server.getProjectId(),
"x-instance-id-signature": digest}
request = requests.get(url, headers=headers, timeout=self.timeout)
if request.status_code != requests.codes.ok:
if request.status_code == 404:
return None
elif request.status_code == 403:
if "Invalid proxy request signature" in request._content:
raise Exception("cannot retrieve the 'userdata' value: "
"check the 'metadata_proxy_shared_secret'"
" attribute value")
else:
request.raise_for_status()
else:
request.raise_for_status()
if request.text:
return request.text
else:
return None
def getFlavors(self):
url = "flavors/detail"
try:
response_data = self.getResource(url, method="GET")
except requests.exceptions.HTTPError as ex:
response = ex.response.json()
raise Exception("error on retrieving the flavors list: %s"
% response)
flavors = []
if response_data:
for flavor_data in response_data["flavors"]:
flavor = Flavor()
flavor.setId(flavor_data["id"])
flavor.setName(flavor_data["name"])
flavor.setVCPUs(flavor_data["vcpus"])
flavor.setMemory(flavor_data["ram"])
flavor.setStorage(flavor_data["disk"])
flavors.append(flavor)
return flavors
def getFlavor(self, id):
try:
response_data = self.getResource("flavors/" + id, "GET")
except requests.exceptions.HTTPError as ex:
raise Exception("error on retrieving the flavor info (id=%r)"
": %s" % (id, ex.response.json()))
flavor = None
if response_data:
flavor_data = response_data["flavor"]
flavor = Flavor()
flavor.setId(flavor_data["id"])
flavor.setName(flavor_data["name"])
flavor.setVCPUs(flavor_data["vcpus"])
flavor.setMemory(flavor_data["ram"])
flavor.setStorage(flavor_data["disk"])
return flavor
def getServers(self, detail=False, status=None):
params = {}
if status:
params["status"] = status
url = "servers/detail"
try:
response_data = self.getResource(url, "GET", params)
except requests.exceptions.HTTPError as ex:
response = ex.response.json()
raise Exception("error on retrieving the servers list"
": %s" % (id, response))
servers = []
if response_data:
for server_data in response_data["servers"]:
server = Server()
server.setId(server_data["id"])
server.setName(server_data["name"])
server.setKeyName(server_data["key_name"])
server.setMetadata(server_data["metadata"])
server.setState(server_data["OS-EXT-STS:vm_state"])
server.setUserId(server_data["user_id"])
server.setProjectId(server_data["tenant_id"])
server.setCreatedAt(server_data["created"])
server.setUpdatedAt(server_data.get("updated", None))
server.setLaunchedAt(
server_data.get("OS-SRV-USG:launched_at", None))
server.setTerminatedAt(
server_data.get("OS-SRV-USG:terminated_at", None))
if "user_data" in server_data:
user_data = server_data["user_data"]
server.setUserData(utils.decodeBase64(user_data))
if detail:
server.setFlavor(self.getFlavor(
server_data["flavor"]["id"]))
servers.append(server)
return servers
def getServer(self, id, detail=False):
try:
response_data = self.getResource("servers/" + id, "GET")
except requests.exceptions.HTTPError as ex:
raise Exception("error on retrieving the server info (id=%r)"
": %s" % (id, ex.response.json()))
server = None
if response_data:
server_data = response_data["server"]
server = Server()
server.setId(server_data["id"])
server.setName(server_data["name"])
server.setKeyName(server_data["key_name"])
server.setMetadata(server_data["metadata"])
server.setState(server_data["OS-EXT-STS:vm_state"])
server.setUserId(server_data["user_id"])
server.setProjectId(server_data["tenant_id"])
server.setCreatedAt(server_data["created"])
server.setUpdatedAt(server_data.get("updated", None))
server.setLaunchedAt(
server_data.get("OS-SRV-USG:launched_at", None))
server.setTerminatedAt(
server_data.get("OS-SRV-USG:terminated_at", None))
if "user_data" in server_data:
user_data = server_data["user_data"]
server.setUserData(utils.decodeBase64(user_data))
if detail:
server.setFlavor(self.getFlavor(server_data["flavor"]["id"]))
return server
def buildServer(self, request, compute=None):
if compute:
reqId = request.getId()
self.novaComputeAPI.build_and_run_instance(
request.getContext(),
request.getInstance(),
compute.getHost(),
request.getImage(),
request.getInstance(),
request.getFilterProperties(),
admin_password=request.getAdminPassword(),
injected_files=request.getInjectedFiles(),
requested_networks=request.getRequestedNetworks(),
security_groups=request.getSecurityGroups(),
block_device_mapping=self.getBlockDeviceMappingList(reqId),
node=compute.getNodeName(),
limits=compute.getLimits())
else:
self.novaConductorComputeAPI.build_instance(
context=request.getContext(),
instance=request.getInstance(),
image=request.getImage(),
filter_properties=request.getFilterProperties(),
admin_password=request.getAdminPassword(),
injected_files=request.getInjectedFiles(),
requested_networks=request.getRequestedNetworks(),
security_groups=request.getSecurityGroups(),
block_device_mapping=request.getBlockDeviceMapping(),
legacy_bdm=request.getLegacyBDM())
def deleteServer(self, server):
if not server:
return
id = server.getId()
url = "servers/%s" % id
try:
response_data = self.getResource(url, "DELETE")
except requests.exceptions.HTTPError as ex:
raise Exception("error on deleting the server (id=%r)"
": %s" % (id, ex.response.json()))
if response_data:
response_data = response_data["server"]
return response_data
def startServer(self, server):
if not server:
return
id = server.getId()
data = {"os-start": None}
url = "servers/%s/action" % id
try:
response_data = self.getResource(url, "POST", data)
except requests.exceptions.HTTPError as ex:
raise Exception("error on starting the server info (id=%r)"
": %s" % (id, ex.response.json()))
if response_data:
response_data = response_data["server"]
return response_data
def setQuotaTypeServer(self, server):
if not server:
return
QUERY = "insert into nova.instance_metadata (created_at, `key`, " \
"`value`, instance_uuid) values (%s, 'quota', %s, %s)"
connection = self.db_engine.connect()
trans = connection.begin()
quota_type = "private"
if server.isEphemeral():
quota_type = "shared"
try:
connection.execute(QUERY,
[server.getCreatedAt(), quota_type,
server.getId()])
trans.commit()
except SQLAlchemyError as ex:
trans.rollback()
raise Exception(ex.message)
finally:
connection.close()
def stopServer(self, server):
if not server:
return
id = server.getId()
data = {"os-stop": None}
url = "servers/%s/action" % id
try:
response_data = self.getResource(url, "POST", data)
except requests.exceptions.HTTPError as ex:
raise Exception("error on stopping the server info (id=%r)"
": %s" % (id, ex.response.json()))
if response_data:
response_data = response_data["server"]
return response_data
def getHosts(self):
data = {}
url = "os-hosts"
# /%s" % id
try:
response_data = self.getResource(url, "GET", data)
except requests.exceptions.HTTPError as ex:
response = ex.response.json()
raise Exception("error on retrieving the hypervisors list: %s"
% response["badRequest"]["message"])
if response_data:
response_data = response_data["hosts"]
return response_data
def getHost(self, name):
data = {}
url = "os-hosts/%s" % name
try:
response_data = self.getResource(url, "GET", data)
except requests.exceptions.HTTPError as ex:
response = ex.response.json()
raise Exception("error on retrieving the hypervisor info (id=%r)"
": %s" % (id, response["badRequest"]["message"]))
if response_data:
response_data = response_data["host"]
return response_data
def getHypervisors(self):
data = {"os-stop": None}
url = "os-hypervisors/detail"
try:
response_data = self.getResource(url, "GET", data)
except requests.exceptions.HTTPError as ex:
LOG.info(ex)
response = ex.response.json()
raise Exception("error on retrieving the hypervisors list: %s"
% response["badRequest"]["message"])
hypervisors = []
if response_data:
hypervisors_data = response_data["hypervisors"]
for hypervisor_data in hypervisors_data:
hypervisor = Hypervisor()
hypervisor.setId(hypervisor_data["id"])
hypervisor.setIP(hypervisor_data["host_ip"])
hypervisor.setName(hypervisor_data["hypervisor_hostname"])
hypervisor.setType(hypervisor_data["hypervisor_type"])
hypervisor.setState(hypervisor_data["state"])
hypervisor.setStatus(hypervisor_data["status"])
hypervisor.setWorkload(hypervisor_data["current_workload"])
hypervisor.setVMs(hypervisor_data["running_vms"])
hypervisor.setVCPUs(hypervisor_data["vcpus"])
hypervisor.setVCPUs(hypervisor_data["vcpus_used"], used=True)
hypervisor.setMemory(hypervisor_data["memory_mb"])
hypervisor.setMemory(hypervisor_data["memory_mb_used"], used=True)
hypervisor.setStorage(hypervisor_data["local_gb"])
hypervisor.setStorage(hypervisor_data["local_gb_used"], used=True)
hypervisors.append(hypervisor)
return hypervisors
def getHypervisor(self, id):
data = {"os-stop": None}
url = "os-hypervisors/%s" % id
try:
response_data = self.getResource(url, "GET", data)
except requests.exceptions.HTTPError as ex:
raise Exception("error on retrieving the hypervisor info (id=%r)"
": %s" % (id, ex.response.json()))
hypervisor = None
if response_data:
hypervisor_data = response_data["hypervisor"]
hypervisor = Hypervisor()
hypervisor.setId(hypervisor_data["id"])
hypervisor.setIP(hypervisor_data["host_ip"])
hypervisor.setName(hypervisor_data["hypervisor_hostname"])
hypervisor.setType(hypervisor_data["hypervisor_type"])
hypervisor.setState(hypervisor_data["state"])
hypervisor.setStatus(hypervisor_data["status"])
hypervisor.setWorkload(hypervisor_data["current_workload"])
hypervisor.setVMs(hypervisor_data["running_vms"])
hypervisor.setVCPUs(hypervisor_data["vcpus"])
hypervisor.setVCPUs(hypervisor_data["vcpus_used"], used=True)
hypervisor.setMemory(hypervisor_data["memory_mb"])
hypervisor.setMemory(hypervisor_data["memory_mb_used"], used=True)
hypervisor.setStorage(hypervisor_data["local_gb"])
hypervisor.setStorage(hypervisor_data["local_gb_used"], used=True)
return hypervisor
def getQuota(self, id=None, is_class=False, defaults=False):
if defaults:
try:
url = "os-quota-sets/defaults"
response_data = self.getResource(url, "GET")
except requests.exceptions.HTTPError as ex:
raise Exception("error on retrieving the quota defaults"
": %s" % ex.response.json())
elif id is not None:
if is_class:
url = "os-quota-class-sets/%s" % id
else:
url = "os-quota-sets/%s" % id
try:
response_data = self.getResource(url, "GET")
if is_class:
quota_data = response_data["quota_class_set"]
else:
quota_data = response_data["quota_set"]
except requests.exceptions.HTTPError as ex:
raise Exception("error on retrieving the quota info (id=%r)"
": %s" % (id, ex.response.json()))
else:
raise Exception("wrong arguments")
quota = None
if quota_data:
quota = Quota()
quota.setId(id)
quota.setSize("vcpus", quota_data["cores"])
quota.setSize("memory", quota_data["ram"])
quota.setSize("instances", quota_data["instances"])
return quota
def updateQuota(self, quota, is_class=False):
if is_class:
url = "os-quota-class-sets/%s" % quota.getId()
qs = {"quota_class_set": {"cores": quota.getSize("vcpus"),
"ram": quota.getSize("memory"),
"instances": quota.getSize("instances")}}
else:
url = "os-quota-sets/%s" % quota.getId()
qs = {"quota_set": {"force": True,
"cores": quota.getSize("vcpus"),
"ram": quota.getSize("memory"),
"instances": quota.getSize("instances")}}
try:
self.getResource(url, "PUT", qs)
except requests.exceptions.HTTPError as ex:
raise Exception("error on updating the quota info (id=%r)"
": %s" % (id, ex.response.json()))
def getResource(self, resource, method, data=None):
self.keystone_manager.authenticate()
token = self.keystone_manager.getToken()
service = token.getService("nova")
if not service:
raise Exception("nova service not found!")
endpoint = service.getEndpoint("public")
if not endpoint:
raise Exception("nova endpoint not found!")
url = endpoint.getURL() + "/" + resource
headers = {"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": "python-novaclient",
"X-Auth-Project-Id": token.getProject().getName(),
"X-Auth-Token": token.getId()}
if method == "GET":
request = requests.get(url, headers=headers,
params=data, timeout=self.timeout)
elif method == "POST":
request = requests.post(url,
headers=headers,
data=json.dumps(data),
timeout=self.timeout)
elif method == "PUT":
request = requests.put(url,
headers=headers,
data=json.dumps(data),
timeout=self.timeout)
elif method == "HEAD":
request = requests.head(url,
headers=headers,
data=json.dumps(data),
timeout=self.timeout)
elif method == "DELETE":
request = requests.delete(url,
headers=headers,
data=json.dumps(data),
timeout=self.timeout)
else:
raise Exception("wrong HTTP method: %s" % method)
if request.status_code != requests.codes.ok:
request.raise_for_status()
if request.text:
return request.json()
else:
return None
def getTarget(self, topic, exchange=None, namespace=None,
version=None, server=None):
return self.messagingAPI.getTarget(topic=topic,
namespace=namespace,
exchange=exchange,
version=version,
server=server)
def getRPCClient(self, target, version_cap=None, serializer=None):
return self.messagingAPI.getRPCClient(target,
version_cap=version_cap,
serializer=serializer)
def getRPCServer(self, target, endpoints, serializer=None):
return self.messagingAPI.getRPCServer(target,
endpoints,
serializer=serializer)
def getNotificationListener(self, targets, endpoints):
return self.messagingAPI.getNotificationListener(targets, endpoints)
def getProjectUsage(self, prj_id, from_date, to_date):
usage = {}
connection = self.db_engine.connect()
try:
QUERY = """select a.user_id, sum(TIMESTAMPDIFF(\
SECOND, IF(a.launched_at<='%(from_date)s', '%(from_date)s', IFNULL(\
a.launched_at, '%(from_date)s')), IF(a.terminated_at>='%(to_date)s', \
'%(to_date)s', IFNULL(a.terminated_at, '%(to_date)s')))*a.memory_mb) as \
memory_usage, sum(TIMESTAMPDIFF(SECOND, IF(a.launched_at<='%(from_date)s', \
'%(from_date)s', IFNULL(a.launched_at, '%(from_date)s')), IF(a.terminated_at\
>='%(to_date)s', '%(to_date)s', IFNULL(a.terminated_at, '%(to_date)s')))\
*a.vcpus) as vcpus_usage from nova.instances as a LEFT OUTER JOIN \
nova.instance_metadata as b ON a.uuid=b.instance_uuid where a.project_id\
='%(prj_id)s' and b.value='shared' and a.launched_at is not NULL and \
a.launched_at<='%(to_date)s' and (a.terminated_at>='%(from_date)s' or \
a.terminated_at is NULL) group by user_id
""" % {"prj_id": prj_id, "from_date": from_date, "to_date": to_date}
LOG.debug("persistent servers query: %s" % QUERY)
result = connection.execute(QUERY)
# for row in result.fetchall():
for row in result:
usage[row[0]] = {"memory": float(row[1]),
"vcpus": float(row[2])}
except SQLAlchemyError as ex:
raise Exception(ex.message)
finally:
connection.close()
return usage
def getProjectServers(self, prj_id):
connection = self.db_engine.connect()
servers = []
try:
# retrieve the amount of resources in terms of cores and memory
QUERY = """select a.uuid, a.vcpus, a.memory_mb, a.root_gb, \
a.vm_state from nova.instances as a WHERE a.project_id='%(project_id)s' \
and a.vm_state in ('active', 'building', 'error') and a.deleted_at is NULL \
and a.terminated_at is NULL""" % {"project_id": prj_id}
LOG.debug("getProjectServers query: %s" % QUERY)
result = connection.execute(QUERY)
for row in result.fetchall():
flavor = Flavor()
flavor.setVCPUs(row[1])
flavor.setMemory(row[2])
flavor.setStorage(row[3])
server = Server()
server.setId(row[0])
server.setState(row[4])
server.setFlavor(flavor)
QUERY = """select `key`, value from nova.instance_metadata \
where instance_uuid='%(id)s' and deleted_at is NULL""" % {"id": server.getId()}
LOG.debug("getProjectServers query: %s" % QUERY)
result = connection.execute(QUERY)
metadata = {}
for row in result.fetchall():
metadata[row[0]] = row[1]
server.setMetadata(metadata)
servers.append(server)
except SQLAlchemyError as ex:
raise Exception(ex.message)
finally:
connection.close()
return servers
def getExpiredServers(self, prj_id, server_ids, TTL):
servers = []
connection = self.db_engine.connect()
try:
# retrieve all expired instances for the specified
# project and expiration time
ids = ""
if server_ids:
ids = "uuid in ('%s') and " % "', '".join(server_ids)
QUERY = """select uuid, vcpus, memory_mb, root_gb, \
vm_state from nova.instances where project_id = \
'%(project_id)s' and deleted_at is NULL and (vm_state='error' or \
(%(server_ids)s vm_state='active' and terminated_at is NULL \
and timestampdiff(minute, launched_at, utc_timestamp()) >= %(expiration)s))\
""" % {"project_id": prj_id, "server_ids": ids, "expiration": TTL}
LOG.debug("getExpiredServers query: %s" % QUERY)
result = connection.execute(QUERY)
for row in result.fetchall():
flavor = Flavor()
flavor.setVCPUs(row[1])
flavor.setMemory(row[2])
flavor.setStorage(row[3])
server = Server()
server.setId(row[0])
server.setState(row[4])
server.setFlavor(flavor)
QUERY = """select `key`, value from nova.instance_metadata \
where instance_uuid='%(id)s' and deleted_at is NULL""" % {"id": server.getId()}
LOG.debug("getExpiredServers query: %s" % QUERY)
result = connection.execute(QUERY)
metadata = {}
for row in result.fetchall():
metadata[row[0]] = row[1]
server.setMetadata(metadata)
servers.append(server)
except SQLAlchemyError as ex:
raise Exception(ex.message)
finally:
connection.close()
return servers
def selectComputes(self, request):
target = self.messagingAPI.getTarget(topic='scheduler',
exchange="nova",
version="4.0")
client = self.messagingAPI.getRPCClient(target)
cctxt = client.prepare(version='4.0')
request_spec = {
'image': request.getImage(),
'instance_properties': request.getInstance(),
'instance_type': request.getFilterProperties()['instance_type'],
'num_instances': 1}
hosts = cctxt.call(request.getContext(),
'select_destinations',
request_spec=request_spec,
filter_properties=request.getFilterProperties())
computes = []
for host in hosts:
compute = Compute()
compute.setHost(host['host'])
compute.setNodeName(host['nodename'])
compute.setLimits(host['limits'])
computes.append(compute)
return computes
def getBlockDeviceMappingList(self, server_id):
connection = self.db_engine.connect()
blockDeviceMapList = []
try:
QUERY = """select id, created_at, updated_at, deleted_at, \
device_name, delete_on_termination, snapshot_id, volume_id, volume_size, \
no_device, connection_info, deleted, source_type, destination_type, \
guest_format, device_type, disk_bus, boot_index, image_id from \
nova.block_device_mapping where instance_uuid='%(server_id)s'
""" % {"server_id": server_id}
LOG.debug("getBlockDeviceMapping query: %s" % QUERY)
result = connection.execute(QUERY)
for row in result.fetchall():
blockDeviceMap = BlockDeviceMapping(row[0])
blockDeviceMap.setCreatedAt(row[1])
blockDeviceMap.setUpdatedAt(row[2])
blockDeviceMap.setDeletedAt(row[3])
blockDeviceMap.setDeviceName(row[4])
blockDeviceMap.setDeleteOnTermination(row[5])
blockDeviceMap.setSnapshotId(row[6])
blockDeviceMap.setVolumeId(row[7])
blockDeviceMap.setVolumeSize(row[8])
blockDeviceMap.setNoDevice(row[9])
blockDeviceMap.setConnectionInfo(row[10])
blockDeviceMap.setDeleted(row[11])
blockDeviceMap.setSourceType(row[12])
blockDeviceMap.setDestinationType(row[13])
blockDeviceMap.setGuestFormat(row[14])
blockDeviceMap.setDeviceType(row[15])
blockDeviceMap.setDiskBus(row[16])
blockDeviceMap.setBootIndex(row[17])
blockDeviceMap.setImageId(row[18])
blockDeviceMap.setInstanceId(server_id)
blockDeviceMapList.append(blockDeviceMap)
except SQLAlchemyError as ex:
raise Exception(ex.message)
finally:
connection.close()
return blockDeviceMapList