302 lines
9.4 KiB
Python
302 lines
9.4 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
# -*- encoding: utf-8 -*-
|
|
#
|
|
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# 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.
|
|
|
|
"""SQLAlchemy storage backend."""
|
|
|
|
from oslo.config import cfg
|
|
|
|
# TODO(deva): import MultipleResultsFound and handle it appropriately
|
|
from sqlalchemy.orm.exc import NoResultFound
|
|
|
|
from ironic.common import exception
|
|
from ironic.common import states
|
|
from ironic.common import utils
|
|
from ironic.db import api
|
|
from ironic.db.sqlalchemy import models
|
|
from ironic import objects
|
|
from ironic.openstack.common.db.sqlalchemy import session as db_session
|
|
from ironic.openstack.common import log
|
|
from ironic.openstack.common import uuidutils
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_opt('connection',
|
|
'ironic.openstack.common.db.sqlalchemy.session',
|
|
group='database')
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
get_engine = db_session.get_engine
|
|
get_session = db_session.get_session
|
|
|
|
|
|
def get_backend():
|
|
"""The backend is this module itself."""
|
|
return Connection()
|
|
|
|
|
|
def model_query(model, *args, **kwargs):
|
|
"""Query helper for simpler session usage.
|
|
|
|
:param session: if present, the session to use
|
|
"""
|
|
|
|
session = kwargs.get('session') or get_session()
|
|
query = session.query(model, *args)
|
|
return query
|
|
|
|
|
|
def add_identity_filter(query, value):
|
|
"""Adds an identity filter to a query.
|
|
|
|
Filters results by ID, if supplied value is a valid integer.
|
|
Otherwise attempts to filter results by UUID.
|
|
|
|
:param query: Initial query to add filter to.
|
|
:param value: Value for filtering results by.
|
|
:return: Modified query.
|
|
"""
|
|
if utils.is_int_like(value):
|
|
return query.filter_by(id=value)
|
|
elif uuidutils.is_uuid_like(value):
|
|
return query.filter_by(uuid=value)
|
|
else:
|
|
raise exception.InvalidIdentity(identity=value)
|
|
|
|
|
|
def add_port_filter(query, value):
|
|
"""Adds a port-specific filter to a query.
|
|
|
|
Filters results by address, if supplied value is a valid MAC
|
|
address. Otherwise attempts to filter results by identity.
|
|
|
|
:param query: Initial query to add filter to.
|
|
:param value: Value for filtering results by.
|
|
:return: Modified query.
|
|
"""
|
|
if utils.is_valid_mac(value):
|
|
return query.filter_by(address=value)
|
|
else:
|
|
return add_identity_filter(query, value)
|
|
|
|
|
|
class Connection(api.Connection):
|
|
"""SqlAlchemy connection."""
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@objects.objectify(objects.Node)
|
|
def get_nodes(self, columns):
|
|
pass
|
|
|
|
def get_node_list(self):
|
|
query = model_query(models.Node.uuid)
|
|
return [i[0] for i in query.all()]
|
|
|
|
@objects.objectify(objects.Node)
|
|
def get_associated_nodes(self):
|
|
pass
|
|
|
|
@objects.objectify(objects.Node)
|
|
def get_unassociated_nodes(self):
|
|
pass
|
|
|
|
@objects.objectify(objects.Node)
|
|
def reserve_nodes(self, tag, nodes):
|
|
# Ensure consistent sort order so we don't run into deadlocks.
|
|
nodes.sort()
|
|
|
|
result = []
|
|
session = get_session()
|
|
with session.begin():
|
|
# TODO(deva): Optimize this by trying to reserve all the nodes
|
|
# at once, and fall back to reserving one at a time
|
|
# only if needed to determine the cause of an error.
|
|
for node in nodes:
|
|
query = model_query(models.Node, session=session)
|
|
query = add_identity_filter(query, node)
|
|
|
|
# Be optimistic and assume we usually get a reservation.
|
|
count = query.filter_by(reservation=None).\
|
|
update({'reservation': tag})
|
|
|
|
if count != 1:
|
|
try:
|
|
ref = query.one()
|
|
except NoResultFound:
|
|
raise exception.NodeNotFound(node=node)
|
|
else:
|
|
raise exception.NodeLocked(node=node)
|
|
ref = query.one()
|
|
result.append(ref)
|
|
|
|
return result
|
|
|
|
def release_nodes(self, tag, nodes):
|
|
session = get_session()
|
|
with session.begin():
|
|
# TODO(deva): Optimize this by trying to release all the nodes
|
|
# at once, and fall back to releasing one at a time
|
|
# only if needed to determine the cause of an error.
|
|
for node in nodes:
|
|
query = model_query(models.Node, session=session)
|
|
query = add_identity_filter(query, node)
|
|
|
|
# be optimistic and assume we usually release a reservation
|
|
count = query.filter_by(reservation=tag).\
|
|
update({'reservation': None})
|
|
|
|
if count != 1:
|
|
try:
|
|
ref = query.one()
|
|
except NoResultFound:
|
|
raise exception.NodeNotFound(node=node)
|
|
else:
|
|
if ref['reservation'] is not None:
|
|
raise exception.NodeLocked(node=node)
|
|
|
|
@objects.objectify(objects.Node)
|
|
def create_node(self, values):
|
|
# ensure defaults are present for new nodes
|
|
if not values.get('uuid'):
|
|
values['uuid'] = uuidutils.generate_uuid()
|
|
if not values.get('task_state'):
|
|
values['task_state'] = states.NOSTATE
|
|
if not values.get('properties'):
|
|
values['properties'] = '{}'
|
|
if not values.get('extra'):
|
|
values['extra'] = '{}'
|
|
if not values.get('driver_info'):
|
|
values['driver_info'] = '{}'
|
|
|
|
node = models.Node()
|
|
node.update(values)
|
|
node.save()
|
|
return node
|
|
|
|
@objects.objectify(objects.Node)
|
|
def get_node(self, node):
|
|
query = model_query(models.Node)
|
|
query = add_identity_filter(query, node)
|
|
|
|
try:
|
|
result = query.one()
|
|
except NoResultFound:
|
|
raise exception.NodeNotFound(node=node)
|
|
|
|
return result
|
|
|
|
@objects.objectify(objects.Node)
|
|
def get_node_by_instance(self, instance):
|
|
query = model_query(models.Node)
|
|
if uuidutils.is_uuid_like(instance):
|
|
query = query.filter_by(instance_uuid=instance)
|
|
else:
|
|
query = query.filter_by(instance_name=instance)
|
|
|
|
try:
|
|
result = query.one()
|
|
except NoResultFound:
|
|
raise exception.InstanceNotFound(instance=instance)
|
|
|
|
return result
|
|
|
|
def destroy_node(self, node):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.Node, session=session)
|
|
query = add_identity_filter(query, node)
|
|
|
|
count = query.delete()
|
|
if count != 1:
|
|
raise exception.NodeNotFound(node=node)
|
|
|
|
@objects.objectify(objects.Node)
|
|
def update_node(self, node, values):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.Node, session=session)
|
|
query = add_identity_filter(query, node)
|
|
|
|
count = query.update(values, synchronize_session='fetch')
|
|
if count != 1:
|
|
raise exception.NodeNotFound(node=node)
|
|
ref = query.one()
|
|
return ref
|
|
|
|
@objects.objectify(objects.Port)
|
|
def get_port(self, port):
|
|
query = model_query(models.Port)
|
|
query = add_port_filter(query, port)
|
|
|
|
try:
|
|
result = query.one()
|
|
except NoResultFound:
|
|
raise exception.PortNotFound(port=port)
|
|
|
|
return result
|
|
|
|
@objects.objectify(objects.Port)
|
|
def get_port_by_vif(self, vif):
|
|
pass
|
|
|
|
@objects.objectify(objects.Port)
|
|
def get_ports_by_node(self, node):
|
|
session = get_session()
|
|
|
|
if utils.is_int_like(node):
|
|
query = session.query(models.Port).\
|
|
filter_by(node_id=node)
|
|
else:
|
|
query = session.query(models.Port).\
|
|
join(models.Node,
|
|
models.Port.node_id == models.Node.id).\
|
|
filter(models.Node.uuid == node)
|
|
result = query.all()
|
|
|
|
return result
|
|
|
|
@objects.objectify(objects.Port)
|
|
def create_port(self, values):
|
|
port = models.Port()
|
|
port.update(values)
|
|
port.save()
|
|
return port
|
|
|
|
@objects.objectify(objects.Port)
|
|
def update_port(self, port, values):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.Port, session=session)
|
|
query = add_port_filter(query, port)
|
|
|
|
count = query.update(values)
|
|
if count != 1:
|
|
raise exception.PortNotFound(port=port)
|
|
ref = query.one()
|
|
return ref
|
|
|
|
def destroy_port(self, port):
|
|
session = get_session()
|
|
with session.begin():
|
|
query = model_query(models.Port, session=session)
|
|
query = add_port_filter(query, port)
|
|
|
|
count = query.delete()
|
|
if count != 1:
|
|
raise exception.PortNotFound(port=port)
|