Building instance base codes
Change-Id: Ic34268df71625b39e588179e6586ca02466b5c6e
This commit is contained in:
parent
41d7272a4b
commit
d71cb7b50c
@ -248,6 +248,13 @@
|
||||
# Deprecated group/name - [DEFAULT]/rpc_zmq_serialization
|
||||
#rpc_zmq_serialization = json
|
||||
|
||||
# This option configures round-robin mode in zmq socket. True
|
||||
# means not keeping a queue when server side disconnects.
|
||||
# False means to keep queue and messages even if server is
|
||||
# disconnected, when the server appears we send all
|
||||
# accumulated messages to it. (boolean value)
|
||||
#zmq_immediate = false
|
||||
|
||||
# Size of executor thread pool. (integer value)
|
||||
# Deprecated group/name - [DEFAULT]/rpc_thread_pool_size
|
||||
#executor_thread_pool_size = 64
|
||||
@ -565,6 +572,82 @@
|
||||
#sync_node_resource_interval = 60
|
||||
|
||||
|
||||
[ironic]
|
||||
|
||||
#
|
||||
# From nimble
|
||||
#
|
||||
|
||||
# URL for the Ironic API endpoint (string value)
|
||||
#api_endpoint = http://ironic.example.org:6385/
|
||||
|
||||
# Ironic keystone admin username (string value)
|
||||
#admin_username = <None>
|
||||
|
||||
# Ironic keystone admin password (string value)
|
||||
#admin_password = <None>
|
||||
|
||||
# DEPRECATED:
|
||||
# Ironic keystone auth token. This option is deprecated and
|
||||
# admin_username, admin_password and admin_tenant_name options
|
||||
# are used for authorization.
|
||||
# (string value)
|
||||
# This option is deprecated for removal.
|
||||
# Its value may be silently ignored in the future.
|
||||
#admin_auth_token = <None>
|
||||
|
||||
# Keystone public API endpoint (string value)
|
||||
#admin_url = <None>
|
||||
|
||||
#
|
||||
# Path to the PEM encoded Certificate Authority file to be
|
||||
# used when verifying
|
||||
# HTTPs connections with the Ironic driver. By default this
|
||||
# option is not used.
|
||||
#
|
||||
# Possible values:
|
||||
#
|
||||
# * None - Default
|
||||
# * Path to the CA file
|
||||
# (string value)
|
||||
#cafile = <None>
|
||||
|
||||
# Ironic keystone tenant name (string value)
|
||||
#admin_tenant_name = <None>
|
||||
|
||||
#
|
||||
# The number of times to retry when a request conflicts.
|
||||
# If set to 0, only try once, no retries.
|
||||
#
|
||||
# Related options:
|
||||
#
|
||||
# * api_retry_interval
|
||||
# (integer value)
|
||||
# Minimum value: 0
|
||||
#api_max_retries = 60
|
||||
|
||||
#
|
||||
# The number of seconds to wait before retrying the request.
|
||||
#
|
||||
# Related options:
|
||||
#
|
||||
# * api_max_retries
|
||||
# (integer value)
|
||||
# Minimum value: 0
|
||||
#api_retry_interval = 2
|
||||
|
||||
|
||||
[keystone]
|
||||
|
||||
#
|
||||
# From nimble
|
||||
#
|
||||
|
||||
# The region used for getting endpoints of OpenStack services.
|
||||
# (string value)
|
||||
#region_name = <None>
|
||||
|
||||
|
||||
[matchmaker_redis]
|
||||
|
||||
#
|
||||
@ -606,16 +689,34 @@
|
||||
|
||||
# Time in ms to wait between connection attempts. (integer
|
||||
# value)
|
||||
#wait_timeout = 5000
|
||||
#wait_timeout = 2000
|
||||
|
||||
# Time in ms to wait before the transaction is killed.
|
||||
# (integer value)
|
||||
#check_timeout = 60000
|
||||
#check_timeout = 20000
|
||||
|
||||
# Timeout in ms on blocking socket operations (integer value)
|
||||
#socket_timeout = 10000
|
||||
|
||||
|
||||
[neutron]
|
||||
|
||||
#
|
||||
# From nimble
|
||||
#
|
||||
|
||||
# URL for connecting to neutron. (string value)
|
||||
#url = <None>
|
||||
|
||||
# Timeout value for connecting to neutron in seconds. (integer
|
||||
# value)
|
||||
#url_timeout = 30
|
||||
|
||||
# Client retries in the case of a failed request. (integer
|
||||
# value)
|
||||
#retries = 3
|
||||
|
||||
|
||||
[oslo_concurrency]
|
||||
|
||||
#
|
||||
@ -641,22 +742,8 @@
|
||||
# From oslo.messaging
|
||||
#
|
||||
|
||||
# address prefix used when sending to a specific server
|
||||
# (string value)
|
||||
# Deprecated group/name - [amqp1]/server_request_prefix
|
||||
#server_request_prefix = exclusive
|
||||
|
||||
# address prefix used when broadcasting to all servers (string
|
||||
# value)
|
||||
# Deprecated group/name - [amqp1]/broadcast_prefix
|
||||
#broadcast_prefix = broadcast
|
||||
|
||||
# address prefix when sending to any server in group (string
|
||||
# value)
|
||||
# Deprecated group/name - [amqp1]/group_request_prefix
|
||||
#group_request_prefix = unicast
|
||||
|
||||
# Name for the AMQP container (string value)
|
||||
# Name for the AMQP container. must be globally unique.
|
||||
# Defaults to a generated UUID (string value)
|
||||
# Deprecated group/name - [amqp1]/container_name
|
||||
#container_name = <None>
|
||||
|
||||
@ -716,6 +803,122 @@
|
||||
# Deprecated group/name - [amqp1]/password
|
||||
#password =
|
||||
|
||||
# Seconds to pause before attempting to re-connect. (integer
|
||||
# value)
|
||||
# Minimum value: 1
|
||||
#connection_retry_interval = 1
|
||||
|
||||
# Increase the connection_retry_interval by this many seconds
|
||||
# after each unsuccessful failover attempt. (integer value)
|
||||
# Minimum value: 0
|
||||
#connection_retry_backoff = 2
|
||||
|
||||
# Maximum limit for connection_retry_interval +
|
||||
# connection_retry_backoff (integer value)
|
||||
# Minimum value: 1
|
||||
#connection_retry_interval_max = 30
|
||||
|
||||
# Time to pause between re-connecting an AMQP 1.0 link that
|
||||
# failed due to a recoverable error. (integer value)
|
||||
# Minimum value: 1
|
||||
#link_retry_delay = 10
|
||||
|
||||
# The deadline for an rpc reply message delivery. Only used
|
||||
# when caller does not provide a timeout expiry. (integer
|
||||
# value)
|
||||
# Minimum value: 5
|
||||
#default_reply_timeout = 30
|
||||
|
||||
# The deadline for an rpc cast or call message delivery. Only
|
||||
# used when caller does not provide a timeout expiry. (integer
|
||||
# value)
|
||||
# Minimum value: 5
|
||||
#default_send_timeout = 30
|
||||
|
||||
# The deadline for a sent notification message delivery. Only
|
||||
# used when caller does not provide a timeout expiry. (integer
|
||||
# value)
|
||||
# Minimum value: 5
|
||||
#default_notify_timeout = 30
|
||||
|
||||
# Indicates the addressing mode used by the driver.
|
||||
# Permitted values:
|
||||
# 'legacy' - use legacy non-routable addressing
|
||||
# 'routable' - use routable addresses
|
||||
# 'dynamic' - use legacy addresses if the message bus does
|
||||
# not support routing otherwise use routable addressing
|
||||
# (string value)
|
||||
#addressing_mode = dynamic
|
||||
|
||||
# address prefix used when sending to a specific server
|
||||
# (string value)
|
||||
# Deprecated group/name - [amqp1]/server_request_prefix
|
||||
#server_request_prefix = exclusive
|
||||
|
||||
# address prefix used when broadcasting to all servers (string
|
||||
# value)
|
||||
# Deprecated group/name - [amqp1]/broadcast_prefix
|
||||
#broadcast_prefix = broadcast
|
||||
|
||||
# address prefix when sending to any server in group (string
|
||||
# value)
|
||||
# Deprecated group/name - [amqp1]/group_request_prefix
|
||||
#group_request_prefix = unicast
|
||||
|
||||
# Address prefix for all generated RPC addresses (string
|
||||
# value)
|
||||
#rpc_address_prefix = openstack.org/om/rpc
|
||||
|
||||
# Address prefix for all generated Notification addresses
|
||||
# (string value)
|
||||
#notify_address_prefix = openstack.org/om/notify
|
||||
|
||||
# Appended to the address prefix when sending a fanout
|
||||
# message. Used by the message bus to identify fanout
|
||||
# messages. (string value)
|
||||
#multicast_address = multicast
|
||||
|
||||
# Appended to the address prefix when sending to a particular
|
||||
# RPC/Notification server. Used by the message bus to identify
|
||||
# messages sent to a single destination. (string value)
|
||||
#unicast_address = unicast
|
||||
|
||||
# Appended to the address prefix when sending to a group of
|
||||
# consumers. Used by the message bus to identify messages that
|
||||
# should be delivered in a round-robin fashion across
|
||||
# consumers. (string value)
|
||||
#anycast_address = anycast
|
||||
|
||||
# Exchange name used in notification addresses.
|
||||
# Exchange name resolution precedence:
|
||||
# Target.exchange if set
|
||||
# else default_notification_exchange if set
|
||||
# else control_exchange if set
|
||||
# else 'notify' (string value)
|
||||
#default_notification_exchange = <None>
|
||||
|
||||
# Exchange name used in RPC addresses.
|
||||
# Exchange name resolution precedence:
|
||||
# Target.exchange if set
|
||||
# else default_rpc_exchange if set
|
||||
# else control_exchange if set
|
||||
# else 'rpc' (string value)
|
||||
#default_rpc_exchange = <None>
|
||||
|
||||
# Window size for incoming RPC Reply messages. (integer value)
|
||||
# Minimum value: 1
|
||||
#reply_link_credit = 200
|
||||
|
||||
# Window size for incoming RPC Request messages (integer
|
||||
# value)
|
||||
# Minimum value: 1
|
||||
#rpc_server_credit = 100
|
||||
|
||||
# Window size for incoming Notification messages (integer
|
||||
# value)
|
||||
# Minimum value: 1
|
||||
#notify_server_credit = 100
|
||||
|
||||
|
||||
[oslo_messaging_notifications]
|
||||
|
||||
@ -1113,6 +1316,13 @@
|
||||
# Deprecated group/name - [DEFAULT]/rpc_zmq_serialization
|
||||
#rpc_zmq_serialization = json
|
||||
|
||||
# This option configures round-robin mode in zmq socket. True
|
||||
# means not keeping a queue when server side disconnects.
|
||||
# False means to keep queue and messages even if server is
|
||||
# disconnected, when the server appears we send all
|
||||
# accumulated messages to it. (boolean value)
|
||||
#zmq_immediate = false
|
||||
|
||||
|
||||
[oslo_policy]
|
||||
|
||||
|
@ -33,6 +33,8 @@ class Instance(base.APIBase):
|
||||
between the internal object model and the API representation of
|
||||
a instance.
|
||||
"""
|
||||
id = wsme.wsattr(wtypes.IntegerType(minimum=1))
|
||||
"""The ID of the instance"""
|
||||
|
||||
uuid = types.uuid
|
||||
"""The UUID of the instance"""
|
||||
@ -55,6 +57,15 @@ class Instance(base.APIBase):
|
||||
availability_zone = wtypes.text
|
||||
"""The availability zone of the instance"""
|
||||
|
||||
instance_type_id = wsme.wsattr(wtypes.IntegerType(minimum=1))
|
||||
"""The instance type ID of the instance"""
|
||||
|
||||
image_uuid = types.uuid
|
||||
"""The image UUID of the instance"""
|
||||
|
||||
network_uuid = types.uuid
|
||||
"""The network UUID of the instance"""
|
||||
|
||||
links = wsme.wsattr([link.Link], readonly=True)
|
||||
"""A list containing a self link"""
|
||||
|
||||
@ -129,9 +140,11 @@ class InstanceController(rest.RestController):
|
||||
# Set the HTTP Location Header
|
||||
pecan.response.location = link.build_url('instance', instance_obj.uuid)
|
||||
|
||||
new_instance = pecan.request.rpcapi.create_instance(
|
||||
pecan.request.context, instance_obj)
|
||||
return Instance.convert_with_links(new_instance)
|
||||
pecan.request.rpcapi.create_instance(pecan.request.context,
|
||||
instance_obj)
|
||||
instance_obj.status = 'building'
|
||||
instance_obj.save()
|
||||
return Instance.convert_with_links(instance_obj)
|
||||
|
||||
@expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT)
|
||||
def delete(self, instance_uuid):
|
||||
@ -141,4 +154,5 @@ class InstanceController(rest.RestController):
|
||||
"""
|
||||
rpc_instance = objects.Instance.get(pecan.request.context,
|
||||
instance_uuid)
|
||||
rpc_instance.destroy()
|
||||
pecan.request.rpcapi.delete_instance(pecan.request.context,
|
||||
rpc_instance)
|
||||
|
@ -155,6 +155,10 @@ class InstanceNotFound(NotFound):
|
||||
msg_fmt = _("Instance %(instance)s could not be found.")
|
||||
|
||||
|
||||
class InstanceDeployFailure(Invalid):
|
||||
msg_fmt = _("Failed to deploy instance: %(reason)s")
|
||||
|
||||
|
||||
class NoFreeEngineWorker(TemporaryFailure):
|
||||
_msg_fmt = _('Requested action cannot be performed due to lack of free '
|
||||
'engine workers.')
|
||||
|
@ -1,61 +1,148 @@
|
||||
# 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
|
||||
# Copyright 2016 Huawei Technologies Co.,LTD.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
# 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 ironicclient import client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from nimble.common import keystone
|
||||
from nimble.common import exception
|
||||
from nimble.common.i18n import _
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
# 1.11 is API version, which support 'enroll' state
|
||||
DEFAULT_IRONIC_API_VERSION = '1.11'
|
||||
ironic = None
|
||||
|
||||
IRONIC_GROUP = 'ironic'
|
||||
|
||||
IRONIC_SESSION = None
|
||||
LEGACY_MAP = {
|
||||
'auth_url': 'os_auth_url',
|
||||
'username': 'os_username',
|
||||
'password': 'os_password',
|
||||
'tenant_name': 'os_tenant_name'
|
||||
}
|
||||
# The API version required by the Ironic driver
|
||||
IRONIC_API_VERSION = (1, 21)
|
||||
|
||||
|
||||
def get_client(token=None,
|
||||
api_version=DEFAULT_IRONIC_API_VERSION): # pragma: no cover
|
||||
"""Get Ironic client instance."""
|
||||
# NOTE: To support standalone ironic without keystone
|
||||
if CONF.ironic.auth_strategy == 'noauth':
|
||||
args = {'token': 'noauth',
|
||||
'endpoint': CONF.ironic.ironic_url}
|
||||
class IronicClientWrapper(object):
|
||||
"""Ironic client wrapper class that encapsulates authentication logic."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialise the IronicClientWrapper for use.
|
||||
|
||||
Initialise IronicClientWrapper by loading ironicclient
|
||||
dynamically so that ironicclient is not a dependency for
|
||||
Nimble.
|
||||
"""
|
||||
global ironic
|
||||
if ironic is None:
|
||||
ironic = importutils.import_module('ironicclient')
|
||||
# NOTE(deva): work around a lack of symbols in the current version.
|
||||
if not hasattr(ironic, 'exc'):
|
||||
ironic.exc = importutils.import_module('ironicclient.exc')
|
||||
if not hasattr(ironic, 'client'):
|
||||
ironic.client = importutils.import_module(
|
||||
'ironicclient.client')
|
||||
self._cached_client = None
|
||||
|
||||
def _invalidate_cached_client(self):
|
||||
"""Tell the wrapper to invalidate the cached ironic-client."""
|
||||
self._cached_client = None
|
||||
|
||||
def _get_client(self, retry_on_conflict=True):
|
||||
max_retries = CONF.ironic.api_max_retries if retry_on_conflict else 1
|
||||
retry_interval = (CONF.ironic.api_retry_interval
|
||||
if retry_on_conflict else 0)
|
||||
|
||||
# If we've already constructed a valid, authed client, just return
|
||||
# that.
|
||||
if retry_on_conflict and self._cached_client is not None:
|
||||
return self._cached_client
|
||||
|
||||
auth_token = CONF.ironic.admin_auth_token
|
||||
if auth_token is None:
|
||||
kwargs = {'os_username': CONF.ironic.admin_username,
|
||||
'os_password': CONF.ironic.admin_password,
|
||||
'os_auth_url': CONF.ironic.admin_url,
|
||||
'os_tenant_name': CONF.ironic.admin_tenant_name,
|
||||
'os_service_type': 'baremetal',
|
||||
'os_endpoint_type': 'public',
|
||||
'ironic_url': CONF.ironic.api_endpoint}
|
||||
else:
|
||||
global IRONIC_SESSION
|
||||
if not IRONIC_SESSION:
|
||||
IRONIC_SESSION = keystone.get_session(
|
||||
IRONIC_GROUP, legacy_mapping=LEGACY_MAP)
|
||||
if token is None:
|
||||
args = {'session': IRONIC_SESSION,
|
||||
'region_name': CONF.ironic.os_region}
|
||||
kwargs = {'os_auth_token': auth_token,
|
||||
'ironic_url': CONF.ironic.api_endpoint}
|
||||
|
||||
if CONF.ironic.cafile:
|
||||
kwargs['os_cacert'] = CONF.ironic.cafile
|
||||
# Set the old option for compat with old clients
|
||||
kwargs['ca_file'] = CONF.ironic.cafile
|
||||
|
||||
# Retries for Conflict exception
|
||||
kwargs['max_retries'] = max_retries
|
||||
kwargs['retry_interval'] = retry_interval
|
||||
kwargs['os_ironic_api_version'] = '%d.%d' % IRONIC_API_VERSION
|
||||
try:
|
||||
cli = ironic.client.get_client(IRONIC_API_VERSION[0], **kwargs)
|
||||
# Cache the client so we don't have to reconstruct and
|
||||
# reauthenticate it every time we need it.
|
||||
if retry_on_conflict:
|
||||
self._cached_client = cli
|
||||
|
||||
except ironic.exc.Unauthorized:
|
||||
msg = _("Unable to authenticate Ironic client.")
|
||||
LOG.error(msg)
|
||||
raise exception.NimbleException(msg)
|
||||
|
||||
return cli
|
||||
|
||||
def _multi_getattr(self, obj, attr):
|
||||
"""Support nested attribute path for getattr().
|
||||
|
||||
:param obj: Root object.
|
||||
:param attr: Path of final attribute to get. E.g., "a.b.c.d"
|
||||
|
||||
:returns: The value of the final named attribute.
|
||||
:raises: AttributeError will be raised if the path is invalid.
|
||||
"""
|
||||
for attribute in attr.split("."):
|
||||
obj = getattr(obj, attribute)
|
||||
return obj
|
||||
|
||||
def call(self, method, *args, **kwargs):
|
||||
"""Call an Ironic client method and retry on stale token.
|
||||
|
||||
:param method: Name of the client method to call as a string.
|
||||
:param args: Client method arguments.
|
||||
:param kwargs: Client method keyword arguments.
|
||||
:param retry_on_conflict: Boolean value. Whether the request should be
|
||||
retried in case of a conflict error
|
||||
(HTTP 409) or not. If retry_on_conflict is
|
||||
False the cached instance of the client
|
||||
won't be used. Defaults to True.
|
||||
"""
|
||||
retry_on_conflict = kwargs.pop('retry_on_conflict', True)
|
||||
|
||||
# NOTE(dtantsur): allow for authentication retry, other retries are
|
||||
# handled by ironicclient starting with 0.8.0
|
||||
for attempt in range(2):
|
||||
client = self._get_client(retry_on_conflict=retry_on_conflict)
|
||||
|
||||
try:
|
||||
return self._multi_getattr(client, method)(*args, **kwargs)
|
||||
except ironic.exc.Unauthorized:
|
||||
# In this case, the authorization token of the cached
|
||||
# ironic-client probably expired. So invalidate the cached
|
||||
# client and the next try will start with a fresh one.
|
||||
if not attempt:
|
||||
self._invalidate_cached_client()
|
||||
LOG.debug("The Ironic client became unauthorized. "
|
||||
"Will attempt to reauthorize and try again.")
|
||||
else:
|
||||
ironic_url = IRONIC_SESSION.get_endpoint(
|
||||
service_type=CONF.ironic.os_service_type,
|
||||
endpoint_type=CONF.ironic.os_endpoint_type,
|
||||
region_name=CONF.ironic.os_region
|
||||
)
|
||||
args = {'token': token,
|
||||
'endpoint': ironic_url}
|
||||
args['os_ironic_api_version'] = api_version
|
||||
args['max_retries'] = CONF.ironic.max_retries
|
||||
args['retry_interval'] = CONF.ironic.retry_interval
|
||||
return client.Client(1, **args)
|
||||
# This code should be unreachable actually
|
||||
raise
|
||||
|
@ -11,11 +11,12 @@
|
||||
# under the License.
|
||||
|
||||
from neutronclient.v2_0 import client as clientv20
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nimble.common import keystone
|
||||
from nimble.conf import CONF
|
||||
|
||||
DEFAULT_NEUTRON_URL = 'http://%s:9696' % CONF.my_ip
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_NEUTRON_SESSION = None
|
||||
|
||||
@ -30,14 +31,6 @@ def _get_neutron_session():
|
||||
def get_client(token=None):
|
||||
params = {'retries': CONF.neutron.retries}
|
||||
url = CONF.neutron.url
|
||||
if CONF.neutron.auth_strategy == 'noauth':
|
||||
params['endpoint_url'] = url or DEFAULT_NEUTRON_URL
|
||||
params['auth_strategy'] = 'noauth'
|
||||
params.update({
|
||||
'timeout': CONF.neutron.url_timeout or CONF.neutron.timeout,
|
||||
'insecure': CONF.neutron.insecure,
|
||||
'ca_cert': CONF.neutron.cafile})
|
||||
else:
|
||||
session = _get_neutron_session()
|
||||
if token is None:
|
||||
params['session'] = session
|
||||
@ -55,8 +48,24 @@ def get_client(token=None):
|
||||
params['endpoint_url'] = url or keystone.get_service_url(
|
||||
session, service_type='network')
|
||||
params.update({
|
||||
'timeout': CONF.neutron.url_timeout or CONF.neutron.timeout,
|
||||
'timeout': CONF.neutron.url_timeout,
|
||||
'insecure': CONF.neutron.insecure,
|
||||
'ca_cert': CONF.neutron.cafile})
|
||||
|
||||
return clientv20.Client(**params)
|
||||
|
||||
|
||||
def create_ports(context, network_uuid, macs):
|
||||
"""Create neutron port."""
|
||||
|
||||
client = get_client(context.auth_token)
|
||||
body = {
|
||||
'port': {
|
||||
'network_id': network_uuid,
|
||||
'mac_address': macs
|
||||
}
|
||||
}
|
||||
|
||||
port = client.create_port(body)
|
||||
|
||||
return port
|
||||
|
@ -19,6 +19,9 @@ from nimble.conf import api
|
||||
from nimble.conf import database
|
||||
from nimble.conf import default
|
||||
from nimble.conf import engine
|
||||
from nimble.conf import ironic
|
||||
from nimble.conf import keystone
|
||||
from nimble.conf import neutron
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -26,3 +29,6 @@ api.register_opts(CONF)
|
||||
database.register_opts(CONF)
|
||||
default.register_opts(CONF)
|
||||
engine.register_opts(CONF)
|
||||
ironic.register_opts(CONF)
|
||||
keystone.register_opts(CONF)
|
||||
neutron.register_opts(CONF)
|
||||
|
103
nimble/conf/ironic.py
Normal file
103
nimble/conf/ironic.py
Normal file
@ -0,0 +1,103 @@
|
||||
# Copyright 2015 Intel Corporation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 oslo_config import cfg
|
||||
|
||||
opt_group = cfg.OptGroup(
|
||||
'ironic',
|
||||
title='Ironic Options',
|
||||
help="""
|
||||
Configuration options for Ironic driver (Bare Metal).
|
||||
If using the Ironic driver following options must be set:
|
||||
* admin_url
|
||||
* admin_tenant_name
|
||||
* admin_username
|
||||
* admin_password
|
||||
* api_endpoint
|
||||
""")
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt(
|
||||
# TODO(raj_singh): Get this value from keystone service catalog
|
||||
'api_endpoint',
|
||||
sample_default='http://ironic.example.org:6385/',
|
||||
help='URL for the Ironic API endpoint'),
|
||||
cfg.StrOpt(
|
||||
'admin_username',
|
||||
help='Ironic keystone admin username'),
|
||||
cfg.StrOpt(
|
||||
'admin_password',
|
||||
secret=True,
|
||||
help='Ironic keystone admin password'),
|
||||
cfg.StrOpt(
|
||||
'admin_auth_token',
|
||||
secret=True,
|
||||
deprecated_for_removal=True,
|
||||
help="""
|
||||
Ironic keystone auth token. This option is deprecated and
|
||||
admin_username, admin_password and admin_tenant_name options
|
||||
are used for authorization.
|
||||
"""),
|
||||
cfg.StrOpt(
|
||||
# TODO(raj_singh): Change this option admin_url->auth_url to make it
|
||||
# consistent with other clients (Neutron, Cinder). It requires lot
|
||||
# of work in Ironic client to make this happen.
|
||||
'admin_url',
|
||||
help='Keystone public API endpoint'),
|
||||
cfg.StrOpt(
|
||||
'cafile',
|
||||
default=None,
|
||||
help="""
|
||||
Path to the PEM encoded Certificate Authority file to be used when verifying
|
||||
HTTPs connections with the Ironic driver. By default this option is not used.
|
||||
|
||||
Possible values:
|
||||
|
||||
* None - Default
|
||||
* Path to the CA file
|
||||
"""),
|
||||
cfg.StrOpt(
|
||||
'admin_tenant_name',
|
||||
help='Ironic keystone tenant name'),
|
||||
cfg.IntOpt(
|
||||
'api_max_retries',
|
||||
# TODO(raj_singh): Change this default to some sensible number
|
||||
default=60,
|
||||
min=0,
|
||||
help="""
|
||||
The number of times to retry when a request conflicts.
|
||||
If set to 0, only try once, no retries.
|
||||
|
||||
Related options:
|
||||
|
||||
* api_retry_interval
|
||||
"""),
|
||||
cfg.IntOpt(
|
||||
'api_retry_interval',
|
||||
default=2,
|
||||
min=0,
|
||||
help="""
|
||||
The number of seconds to wait before retrying the request.
|
||||
|
||||
Related options:
|
||||
|
||||
* api_max_retries
|
||||
"""),
|
||||
]
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_group(opt_group)
|
||||
conf.register_opts(opts, group=opt_group)
|
26
nimble/conf/keystone.py
Normal file
26
nimble/conf/keystone.py
Normal file
@ -0,0 +1,26 @@
|
||||
#
|
||||
# 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 oslo_config import cfg
|
||||
|
||||
from nimble.common.i18n import _
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('region_name',
|
||||
help=_('The region used for getting endpoints of OpenStack'
|
||||
' services.')),
|
||||
]
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_opts(opts, group='keystone')
|
41
nimble/conf/neutron.py
Normal file
41
nimble/conf/neutron.py
Normal file
@ -0,0 +1,41 @@
|
||||
#
|
||||
# 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 oslo_config import cfg
|
||||
|
||||
from nimble.common.i18n import _
|
||||
from nimble.conf import auth
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('url',
|
||||
help=_("URL for connecting to neutron.")),
|
||||
cfg.IntOpt('url_timeout',
|
||||
default=30,
|
||||
help=_('Timeout value for connecting to neutron in seconds.')),
|
||||
cfg.IntOpt('retries',
|
||||
default=3,
|
||||
help=_('Client retries in the case of a failed request.')),
|
||||
]
|
||||
|
||||
opt_group = cfg.OptGroup(name='neutron',
|
||||
title='Options for the neutron service')
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_group(opt_group)
|
||||
conf.register_opts(opts, group=opt_group)
|
||||
auth.register_auth_opts(conf, 'neutron')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return auth.add_auth_opts(opts)
|
@ -16,6 +16,9 @@ import nimble.conf.api
|
||||
import nimble.conf.database
|
||||
import nimble.conf.default
|
||||
import nimble.conf.engine
|
||||
import nimble.conf.ironic
|
||||
import nimble.conf.keystone
|
||||
import nimble.conf.neutron
|
||||
|
||||
_default_opt_lists = [
|
||||
nimble.conf.default.api_opts,
|
||||
@ -29,6 +32,9 @@ _opts = [
|
||||
('api', nimble.conf.api.opts),
|
||||
('database', nimble.conf.database.opts),
|
||||
('engine', nimble.conf.engine.opts),
|
||||
('ironic', nimble.conf.ironic.opts),
|
||||
('keystone', nimble.conf.keystone.opts),
|
||||
('neutron', nimble.conf.neutron.opts),
|
||||
]
|
||||
|
||||
|
||||
|
@ -68,6 +68,8 @@ def upgrade():
|
||||
sa.Column('power_state', sa.String(length=255), nullable=True),
|
||||
sa.Column('task_state', sa.String(length=255), nullable=True),
|
||||
sa.Column('instance_type_id', sa.Integer(), nullable=True),
|
||||
sa.Column('image_uuid', sa.String(length=36), nullable=True),
|
||||
sa.Column('network_uuid', sa.String(length=36), nullable=True),
|
||||
sa.Column('launched_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('terminated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('availability_zone', sa.String(length=255), nullable=True),
|
||||
|
@ -104,5 +104,7 @@ class Instance(Base):
|
||||
task_state = Column(String(255), nullable=True)
|
||||
instance_type_id = Column(Integer, nullable=True)
|
||||
availability_zone = Column(String(255), nullable=True)
|
||||
image_uuid = Column(String(36), nullable=True)
|
||||
network_uuid = Column(String(36), nullable=True)
|
||||
node_uuid = Column(String(36), nullable=True)
|
||||
extra = Column(Text, nullable=True)
|
||||
|
0
nimble/engine/baremetal/__init__.py
Normal file
0
nimble/engine/baremetal/__init__.py
Normal file
83
nimble/engine/baremetal/ironic.py
Normal file
83
nimble/engine/baremetal/ironic.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright 2016 Huawei Technologies Co.,LTD.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 nimble.common import ironic
|
||||
from nimble.engine.baremetal import ironic_states
|
||||
|
||||
_NODE_FIELDS = ('uuid', 'power_state', 'target_power_state', 'provision_state',
|
||||
'target_provision_state', 'last_error', 'maintenance',
|
||||
'properties', 'instance_uuid')
|
||||
|
||||
|
||||
def get_macs_from_node(node_uuid):
|
||||
"""List the MAC addresses from a node."""
|
||||
ironicclient = ironic.IronicClientWrapper()
|
||||
ports = ironicclient.call("node.list_ports", node_uuid)
|
||||
return [p.address for p in ports]
|
||||
|
||||
|
||||
def plug_vifs(node_uuid, port_id):
|
||||
ironicclient = ironic.IronicClientWrapper()
|
||||
ports = ironicclient.call("node.list_ports", node_uuid)
|
||||
patch = [{'op': 'add',
|
||||
'path': '/extra/vif_port_id',
|
||||
'value': port_id}]
|
||||
ironicclient.call("port.update", ports[0].uuid, patch)
|
||||
|
||||
|
||||
def set_instance_info(instance):
|
||||
ironicclient = ironic.IronicClientWrapper()
|
||||
|
||||
patch = []
|
||||
# Associate the node with an instance
|
||||
patch.append({'path': '/instance_uuid', 'op': 'add',
|
||||
'value': instance.uuid})
|
||||
# Add the required fields to deploy a node.
|
||||
patch.append({'path': '/instance_info/image_source', 'op': 'add',
|
||||
'value': instance.image_uuid})
|
||||
patch.append({'path': '/instance_info/root_gb', 'op': 'add',
|
||||
'value': '10'})
|
||||
patch.append({'path': '/instance_info/swap_mb', 'op': 'add',
|
||||
'value': '0'})
|
||||
patch.append({'path': '/instance_info/display_name',
|
||||
'op': 'add', 'value': instance.name})
|
||||
patch.append({'path': '/instance_info/vcpus', 'op': 'add',
|
||||
'value': '1'})
|
||||
patch.append({'path': '/instance_info/memory_mb', 'op': 'add',
|
||||
'value': '10240'})
|
||||
patch.append({'path': '/instance_info/local_gb', 'op': 'add',
|
||||
'value': '10'})
|
||||
|
||||
ironicclient.call("node.update", instance.node_uuid, patch)
|
||||
|
||||
|
||||
def do_node_deploy(node_uuid):
|
||||
# trigger the node deploy
|
||||
ironicclient = ironic.IronicClientWrapper()
|
||||
ironicclient.call("node.set_provision_state", node_uuid,
|
||||
ironic_states.ACTIVE)
|
||||
|
||||
|
||||
def get_node_by_instance(instance_uuid):
|
||||
ironicclient = ironic.IronicClientWrapper()
|
||||
return ironicclient.call('node.get_by_instance_uuid',
|
||||
instance_uuid, fields=_NODE_FIELDS)
|
||||
|
||||
|
||||
def destroy_node(node_uuid):
|
||||
# trigger the node destroy
|
||||
ironicclient = ironic.IronicClientWrapper()
|
||||
ironicclient.call("node.set_provision_state", node_uuid,
|
||||
ironic_states.DELETED)
|
156
nimble/engine/baremetal/ironic_states.py
Normal file
156
nimble/engine/baremetal/ironic_states.py
Normal file
@ -0,0 +1,156 @@
|
||||
# Copyright (c) 2012 NTT DOCOMO, INC.
|
||||
# Copyright 2010 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Mapping of bare metal node states.
|
||||
|
||||
Setting the node `power_state` is handled by the conductor's power
|
||||
synchronization thread. Based on the power state retrieved from the driver
|
||||
for the node, the state is set to POWER_ON or POWER_OFF, accordingly.
|
||||
Should this fail, the `power_state` value is left unchanged, and the node
|
||||
is placed into maintenance mode.
|
||||
|
||||
The `power_state` can also be set manually via the API. A failure to change
|
||||
the state leaves the current state unchanged. The node is NOT placed into
|
||||
maintenance mode in this case.
|
||||
"""
|
||||
|
||||
|
||||
#####################
|
||||
# Provisioning states
|
||||
#####################
|
||||
|
||||
NOSTATE = None
|
||||
""" No state information.
|
||||
|
||||
This state is used with power_state to represent a lack of knowledge of
|
||||
power state, and in target_*_state fields when there is no target.
|
||||
|
||||
Prior to the Kilo release, Ironic set node.provision_state to NOSTATE
|
||||
when the node was available for provisioning. During Kilo cycle, this was
|
||||
changed to the AVAILABLE state.
|
||||
"""
|
||||
|
||||
MANAGEABLE = 'manageable'
|
||||
""" Node is in a manageable state.
|
||||
This state indicates that Ironic has verified, at least once, that it had
|
||||
sufficient information to manage the hardware. While in this state, the node
|
||||
is not available for provisioning (it must be in the AVAILABLE state for that).
|
||||
"""
|
||||
|
||||
AVAILABLE = 'available'
|
||||
""" Node is available for use and scheduling.
|
||||
|
||||
This state is replacing the NOSTATE state used prior to Kilo.
|
||||
"""
|
||||
|
||||
ACTIVE = 'active'
|
||||
""" Node is successfully deployed and associated with an instance. """
|
||||
|
||||
DEPLOYWAIT = 'wait call-back'
|
||||
""" Node is waiting to be deployed.
|
||||
|
||||
This will be the node `provision_state` while the node is waiting for
|
||||
the driver to finish deployment.
|
||||
"""
|
||||
|
||||
DEPLOYING = 'deploying'
|
||||
""" Node is ready to receive a deploy request, or is currently being deployed.
|
||||
|
||||
A node will have its `provision_state` set to DEPLOYING briefly before it
|
||||
receives its initial deploy request. It will also move to this state from
|
||||
DEPLOYWAIT after the callback is triggered and deployment is continued
|
||||
(disk partitioning and image copying).
|
||||
"""
|
||||
|
||||
DEPLOYFAIL = 'deploy failed'
|
||||
""" Node deployment failed. """
|
||||
|
||||
DEPLOYDONE = 'deploy complete'
|
||||
""" Node was successfully deployed.
|
||||
|
||||
This is mainly a target provision state used during deployment. A successfully
|
||||
deployed node should go to ACTIVE status.
|
||||
"""
|
||||
|
||||
DELETING = 'deleting'
|
||||
""" Node is actively being torn down. """
|
||||
|
||||
DELETED = 'deleted'
|
||||
""" Node tear down was successful.
|
||||
|
||||
In Juno, target_provision_state was set to this value during node tear down.
|
||||
In Kilo, this will be a transitory value of provision_state, and never
|
||||
represented in target_provision_state.
|
||||
"""
|
||||
|
||||
CLEANING = 'cleaning'
|
||||
""" Node is being automatically cleaned to prepare it for provisioning. """
|
||||
|
||||
CLEANWAIT = 'clean wait'
|
||||
""" Node is waiting for a clean step to be finished.
|
||||
|
||||
This will be the node's `provision_state` while the node is waiting for
|
||||
the driver to finish a cleaning step.
|
||||
"""
|
||||
|
||||
CLEANFAIL = 'clean failed'
|
||||
""" Node failed cleaning. This requires operator intervention to resolve. """
|
||||
|
||||
ERROR = 'error'
|
||||
""" An error occurred during node processing.
|
||||
|
||||
The `last_error` attribute of the node details should contain an error message.
|
||||
"""
|
||||
|
||||
REBUILD = 'rebuild'
|
||||
""" Node is to be rebuilt.
|
||||
This is not used as a state, but rather as a "verb" when changing the node's
|
||||
provision_state via the REST API.
|
||||
"""
|
||||
|
||||
INSPECTING = 'inspecting'
|
||||
""" Node is under inspection.
|
||||
This is the provision state used when inspection is started. A successfully
|
||||
inspected node shall transition to MANAGEABLE status.
|
||||
"""
|
||||
|
||||
INSPECTFAIL = 'inspect failed'
|
||||
""" Node inspection failed. """
|
||||
|
||||
|
||||
##############
|
||||
# Power states
|
||||
##############
|
||||
|
||||
POWER_ON = 'power on'
|
||||
""" Node is powered on. """
|
||||
|
||||
POWER_OFF = 'power off'
|
||||
""" Node is powered off. """
|
||||
|
||||
REBOOT = 'rebooting'
|
||||
""" Node is rebooting. """
|
||||
|
||||
##################
|
||||
# Helper constants
|
||||
##################
|
||||
|
||||
PROVISION_STATE_LIST = (NOSTATE, MANAGEABLE, AVAILABLE, ACTIVE, DEPLOYWAIT,
|
||||
DEPLOYING, DEPLOYFAIL, DEPLOYDONE, DELETING, DELETED,
|
||||
CLEANING, CLEANWAIT, CLEANFAIL, ERROR, REBUILD,
|
||||
INSPECTING, INSPECTFAIL)
|
||||
""" A list of all provision states. """
|
@ -15,10 +15,15 @@
|
||||
|
||||
from oslo_log import log
|
||||
import oslo_messaging as messaging
|
||||
from oslo_service import loopingcall
|
||||
from oslo_service import periodic_task
|
||||
|
||||
from nimble.common import exception
|
||||
from nimble.common.i18n import _LI
|
||||
from nimble.common import neutron
|
||||
from nimble.conf import CONF
|
||||
from nimble.engine.baremetal import ironic
|
||||
from nimble.engine.baremetal import ironic_states
|
||||
from nimble.engine import base_manager
|
||||
|
||||
MANAGER_TOPIC = 'nimble.engine_manager'
|
||||
@ -38,9 +43,74 @@ class EngineManager(base_manager.BaseEngineManager):
|
||||
def _sync_node_resources(self, context):
|
||||
LOG.info(_LI("During sync_node_resources."))
|
||||
|
||||
def _build_networks(self, context, instance):
|
||||
macs = ironic.get_macs_from_node(instance.node_uuid)
|
||||
port = neutron.create_ports(context, instance.network_uuid, macs[0])
|
||||
ironic.plug_vifs(instance.node_uuid, port['port']['id'])
|
||||
|
||||
def _wait_for_active(self, instance):
|
||||
"""Wait for the node to be marked as ACTIVE in Ironic."""
|
||||
|
||||
node = ironic.get_node_by_instance(instance.uuid)
|
||||
LOG.debug('Current ironic node state is %s', node.provision_state)
|
||||
if node.provision_state == ironic_states.ACTIVE:
|
||||
# job is done
|
||||
LOG.debug("Ironic node %(node)s is now ACTIVE",
|
||||
dict(node=node.uuid))
|
||||
instance.status = ironic_states.ACTIVE
|
||||
instance.save()
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
||||
if node.target_provision_state in (ironic_states.DELETED,
|
||||
ironic_states.AVAILABLE):
|
||||
# ironic is trying to delete it now
|
||||
raise exception.InstanceNotFound(instance_id=instance.uuid)
|
||||
|
||||
if node.provision_state in (ironic_states.NOSTATE,
|
||||
ironic_states.AVAILABLE):
|
||||
# ironic already deleted it
|
||||
raise exception.InstanceNotFound(instance_id=instance.uuid)
|
||||
|
||||
if node.provision_state == ironic_states.DEPLOYFAIL:
|
||||
# ironic failed to deploy
|
||||
msg = (_("Failed to provision instance %(inst)s: %(reason)s")
|
||||
% {'inst': instance.uuid, 'reason': node.last_error})
|
||||
raise exception.InstanceDeployFailure(msg)
|
||||
|
||||
def _build_instance(self, context, instance):
|
||||
ironic.set_instance_info(instance)
|
||||
ironic.do_node_deploy(instance.node_uuid)
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(self._wait_for_active,
|
||||
instance)
|
||||
timer.start(interval=CONF.ironic.api_retry_interval).wait()
|
||||
LOG.info(_LI('Successfully provisioned Ironic node %s'),
|
||||
instance.node_uuid)
|
||||
|
||||
def _destroy_instance(self, context, instance):
|
||||
ironic.destroy_node(instance.node_uuid)
|
||||
LOG.info(_LI('Successfully destroyed Ironic node %s'),
|
||||
instance.node_uuid)
|
||||
|
||||
def create_instance(self, context, instance):
|
||||
"""Signal to engine service to perform a deployment."""
|
||||
LOG.debug("During create instance.")
|
||||
instance.task_state = 'deploying'
|
||||
LOG.debug("Strating instance...")
|
||||
instance.status = 'building'
|
||||
|
||||
# Scheduling...
|
||||
# instance.node_uuid = '8d22309b-b47a-41a7-80e3-e758fae9dedd'
|
||||
instance.save()
|
||||
|
||||
self._build_networks(context, instance)
|
||||
|
||||
self._build_instance(context, instance)
|
||||
|
||||
return instance
|
||||
|
||||
def delete_instance(self, context, instance):
|
||||
"""Signal to engine service to delete an instance."""
|
||||
LOG.debug("Deleting instance...")
|
||||
|
||||
self._destroy_instance(context, instance)
|
||||
|
||||
instance.destroy()
|
||||
|
@ -52,4 +52,9 @@ class EngineAPI(object):
|
||||
def create_instance(self, context, instance):
|
||||
"""Signal to engine service to perform a deployment."""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
return cctxt.call(context, 'create_instance', instance=instance)
|
||||
return cctxt.cast(context, 'create_instance', instance=instance)
|
||||
|
||||
def delete_instance(self, context, instance):
|
||||
"""Signal to engine service to delete an instance."""
|
||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||
return cctxt.call(context, 'delete_instance', instance=instance)
|
||||
|
@ -38,6 +38,8 @@ class Instance(base.NimbleObject, object_base.VersionedObjectDictCompat):
|
||||
'task_state': object_fields.StringField(nullable=True),
|
||||
'instance_type_id': object_fields.IntegerField(nullable=True),
|
||||
'availability_zone': object_fields.StringField(nullable=True),
|
||||
'image_uuid': object_fields.UUIDField(nullable=True),
|
||||
'network_uuid': object_fields.UUIDField(nullable=True),
|
||||
'node_uuid': object_fields.UUIDField(nullable=True),
|
||||
'extra': object_fields.FlexibleDictField(nullable=True),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user