Allowing a user to add a volume to the instance on create api call
added some helper code to make sure the mysql service is up add a config value to turn on volume support moving around the lines to be dependent on volume support trying to stop volume support fixes with the merge imported and raised exceptions in guest correctly fixed the config get values as boolean and not just the string deleting the serivce status entry on delete of the instance so that the guest can not update the instance to active again. made the create and update times saved correctly for an instance check if mysql is already installed in the guest clean up instance model using _(i8n) adding the populate databases back in pep8 compliance updated apt get version method moved the volumeHelper class to a new module made the start/stop mysql methods public created a method to create volume for instances and readability created a new client for volume calls added the volume and size to the create/list/show instance api calls removing the mount_point and device_path from the MysqlApp class removing code that deleted the instance status entry adding volume service to the reddwarf.conf.test file set volumes to not be disabled by default
This commit is contained in:
parent
6527d65118
commit
777f492d1b
@ -52,6 +52,13 @@ nova_service_name = Compute Service
|
||||
# Config option for showing the IP address that nova doles out
|
||||
add_addresses = True
|
||||
|
||||
# Config options for enabling volume service
|
||||
reddwarf_volume_support = False
|
||||
nova_volume_service_type = volume
|
||||
nova_volume_service_name = Volume Service
|
||||
device_path = /dev/vdb
|
||||
mount_point = /var/lib/mysql
|
||||
|
||||
# ============ notifer queue kombu connection options ========================
|
||||
|
||||
notifier_queue_hostname = localhost
|
||||
|
@ -52,6 +52,16 @@ nova_region_name = RegionOne
|
||||
nova_service_type = compute
|
||||
nova_service_name = Compute Service
|
||||
|
||||
# Config option for showing the IP address that nova doles out
|
||||
add_addresses = True
|
||||
|
||||
# Config options for enabling volume service
|
||||
reddwarf_volume_support = False
|
||||
nova_volume_service_type = volume
|
||||
nova_volume_service_name = Volume Service
|
||||
device_path = /dev/vdb
|
||||
mount_point = /var/lib/mysql
|
||||
|
||||
# ============ notifer queue kombu connection options ========================
|
||||
|
||||
notifier_queue_hostname = localhost
|
||||
|
@ -87,3 +87,8 @@ class UnprocessableEntity(ReddwarfError):
|
||||
|
||||
message = _("Unable to process the contained request")
|
||||
|
||||
|
||||
class VolumeAttachmentsNotFound(NotFound):
|
||||
|
||||
message = _("Cannot find the volumes attached to compute "
|
||||
"instance %(server_id)")
|
||||
|
@ -19,7 +19,6 @@ from reddwarf.common import config
|
||||
from novaclient.v1_1.client import Client
|
||||
|
||||
|
||||
|
||||
CONFIG = config.Config
|
||||
|
||||
|
||||
@ -40,6 +39,7 @@ def create_nova_client(context):
|
||||
PROXY_AUTH_URL = CONFIG.get('reddwarf_auth_url',
|
||||
'http://0.0.0.0:5000/v2.0')
|
||||
REGION_NAME = CONFIG.get('nova_region_name', 'RegionOne')
|
||||
|
||||
SERVICE_TYPE = CONFIG.get('nova_service_type', 'compute')
|
||||
SERVICE_NAME = CONFIG.get('nova_service_name', 'Compute Service')
|
||||
|
||||
@ -55,10 +55,38 @@ def create_nova_client(context):
|
||||
return client
|
||||
|
||||
|
||||
def create_nova_volume_client(context):
|
||||
# Quite annoying but due to a paste config loading bug.
|
||||
# TODO(hub-cap): talk to the openstack-common people about this
|
||||
PROXY_ADMIN_USER = CONFIG.get('reddwarf_proxy_admin_user', 'admin')
|
||||
PROXY_ADMIN_PASS = CONFIG.get('reddwarf_proxy_admin_pass',
|
||||
'3de4922d8b6ac5a1aad9')
|
||||
PROXY_ADMIN_TENANT_NAME = CONFIG.get('reddwarf_proxy_admin_tenant_name',
|
||||
'admin')
|
||||
PROXY_AUTH_URL = CONFIG.get('reddwarf_auth_url',
|
||||
'http://0.0.0.0:5000/v2.0')
|
||||
REGION_NAME = CONFIG.get('nova_region_name', 'RegionOne')
|
||||
|
||||
SERVICE_TYPE = CONFIG.get('nova_volume_service_type', 'volume')
|
||||
SERVICE_NAME = CONFIG.get('nova_volume_service_name', 'Volume Service')
|
||||
|
||||
#TODO(cp16net) need to fix this proxy_tenant_id
|
||||
client = Client(PROXY_ADMIN_USER, PROXY_ADMIN_PASS,
|
||||
PROXY_ADMIN_TENANT_NAME, PROXY_AUTH_URL,
|
||||
proxy_tenant_id=context.tenant,
|
||||
proxy_token=context.auth_tok,
|
||||
region_name=REGION_NAME,
|
||||
service_type=SERVICE_TYPE,
|
||||
service_name=SERVICE_NAME)
|
||||
client.authenticate()
|
||||
return client
|
||||
|
||||
|
||||
if CONFIG.get("remote_implementation", "real") == "fake":
|
||||
# Override the functions above with fakes.
|
||||
|
||||
from reddwarf.tests.fakes.nova import fake_create_nova_client
|
||||
from reddwarf.tests.fakes.nova import fake_create_nova_volume_client
|
||||
from reddwarf.tests.fakes.guestagent import fake_create_guest_client
|
||||
|
||||
def create_guest_client(context, id):
|
||||
@ -66,3 +94,6 @@ if CONFIG.get("remote_implementation", "real") == "fake":
|
||||
|
||||
def create_nova_client(context):
|
||||
return fake_create_nova_client(context)
|
||||
|
||||
def create_nova_volume_client(context):
|
||||
return fake_create_nova_volume_client(context)
|
||||
|
@ -22,6 +22,7 @@ import logging
|
||||
import re
|
||||
import signal
|
||||
import sys
|
||||
import urlparse
|
||||
import uuid
|
||||
|
||||
from eventlet import event
|
||||
@ -146,6 +147,7 @@ class LoopingCallDone(Exception):
|
||||
|
||||
def __init__(self, retvalue=True):
|
||||
""":param retvalue: Value that LoopingCall.wait() should return."""
|
||||
super(LoopingCallDone, self).__init__()
|
||||
self.retvalue = retvalue
|
||||
|
||||
|
||||
@ -208,10 +210,12 @@ def get_id_from_href(href):
|
||||
|
||||
def execute_with_timeout(*args, **kwargs):
|
||||
time = kwargs.get('timeout', 30)
|
||||
|
||||
def cb_timeout():
|
||||
raise exception.ProcessExecutionError("Time out after waiting "
|
||||
+ str(time) + " seconds when running proc: " + str(args)
|
||||
+ str(kwargs))
|
||||
msg = _("Time out after waiting"
|
||||
" %(time)s seconds when running proc: %(args)s"
|
||||
" %(kwargs)s") % locals()
|
||||
raise exception.ProcessExecutionError(msg)
|
||||
|
||||
timeout = Timeout(time)
|
||||
try:
|
||||
@ -220,8 +224,9 @@ def execute_with_timeout(*args, **kwargs):
|
||||
if t is not timeout:
|
||||
raise
|
||||
else:
|
||||
raise exception.ProcessExecutionError("Time out after waiting "
|
||||
+ str(time) + " seconds when running proc: " + str(args)
|
||||
+ str(kwargs))
|
||||
msg = _("Time out after waiting "
|
||||
"%(time)s seconds when running proc: %(args)s"
|
||||
" %(kwargs)s") % locals()
|
||||
raise exception.ProcessExecutionError(msg)
|
||||
finally:
|
||||
timeout.cancel()
|
||||
|
@ -122,12 +122,14 @@ class API(object):
|
||||
LOG.debug(_("Check diagnostics on Instance %s"), self.id)
|
||||
return self._call("get_diagnostics")
|
||||
|
||||
def prepare(self, memory_mb, databases, users):
|
||||
def prepare(self, memory_mb, databases, users,
|
||||
device_path='/dev/vdb', mount_point='/mnt/volume'):
|
||||
"""Make an asynchronous call to prepare the guest
|
||||
as a database container"""
|
||||
LOG.debug(_("Sending the call to prepare the Guest"))
|
||||
self._cast_with_consumer("prepare", databases=databases,
|
||||
memory_mb=memory_mb, users=users)
|
||||
memory_mb=memory_mb, users=users, device_path=device_path,
|
||||
mount_point=mount_point)
|
||||
|
||||
def restart(self):
|
||||
"""Restart the MySQL server."""
|
||||
@ -147,5 +149,5 @@ class API(object):
|
||||
|
||||
def upgrade(self):
|
||||
"""Make an asynchronous call to self upgrade the guest agent"""
|
||||
LOG.debug(_("Sending an upgrade call to nova-guest %s"), topic)
|
||||
LOG.debug(_("Sending an upgrade call to nova-guest"))
|
||||
self._cast_with_consumer("upgrade")
|
||||
|
@ -28,6 +28,7 @@ handles RPC calls relating to Platform specific operations.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pexpect
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
@ -40,12 +41,15 @@ from sqlalchemy import interfaces
|
||||
from sqlalchemy.sql.expression import text
|
||||
|
||||
from reddwarf import db
|
||||
from reddwarf.common.exception import GuestError
|
||||
from reddwarf.common.exception import ProcessExecutionError
|
||||
from reddwarf.common import config
|
||||
from reddwarf.common import utils
|
||||
from reddwarf.guestagent.db import models
|
||||
from reddwarf.guestagent.volume import VolumeHelper
|
||||
from reddwarf.instance import models as rd_models
|
||||
|
||||
|
||||
ADMIN_USER_NAME = "os_admin"
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLUSH = text("""FLUSH PRIVILEGES;""")
|
||||
@ -61,6 +65,8 @@ TMP_MYCNF = "/tmp/my.cnf.tmp"
|
||||
DBAAS_MYCNF = "/etc/dbaas/my.cnf/my.cnf.%dM"
|
||||
MYSQL_BASE_DIR = "/var/lib/mysql"
|
||||
|
||||
CONFIG = config.Config
|
||||
|
||||
|
||||
def generate_random_password():
|
||||
return str(uuid.uuid4())
|
||||
@ -140,7 +146,6 @@ class MySqlAppStatus(object):
|
||||
self.status = self._load_status()
|
||||
self.restart_mode = False
|
||||
|
||||
|
||||
def begin_mysql_install(self):
|
||||
"""Called right before MySQL is prepared."""
|
||||
self.set_status(rd_models.ServiceStatuses.BUILDING)
|
||||
@ -176,8 +181,8 @@ class MySqlAppStatus(object):
|
||||
except ProcessExecutionError as e:
|
||||
LOG.error("Process execution ")
|
||||
try:
|
||||
out, err = utils.execute_with_timeout("/bin/ps", "-C", "mysqld",
|
||||
"h")
|
||||
out, err = utils.execute_with_timeout("/bin/ps", "-C",
|
||||
"mysqld", "h")
|
||||
pid = out.split()[0]
|
||||
# TODO(rnirmal): Need to create new statuses for instances
|
||||
# where the mysql service is up, but unresponsive
|
||||
@ -228,7 +233,6 @@ class MySqlAppStatus(object):
|
||||
db_status.save()
|
||||
self.status = status
|
||||
|
||||
|
||||
def update(self):
|
||||
"""Find and report status of MySQL on this machine.
|
||||
|
||||
@ -488,13 +492,33 @@ class DBaaSAgent(object):
|
||||
def is_root_enabled(self):
|
||||
return MySqlAdmin().is_root_enabled()
|
||||
|
||||
def prepare(self, databases, memory_mb, users):
|
||||
def prepare(self, databases, memory_mb, users, device_path=None,
|
||||
mount_point=None):
|
||||
"""Makes ready DBAAS on a Guest container."""
|
||||
from reddwarf.guestagent.pkg import PkgAgent
|
||||
if not isinstance(self, PkgAgent):
|
||||
raise TypeError("This must also be an instance of Pkg agent.")
|
||||
pkg = self # Python cast.
|
||||
self.status.begin_mysql_install()
|
||||
# status end_mysql_install set with install_and_secure()
|
||||
app = MySqlApp(self.status)
|
||||
restart_mysql = False
|
||||
if not device_path is None:
|
||||
VolumeHelper.format(device_path)
|
||||
if app.is_installed(pkg):
|
||||
#stop and do not update database
|
||||
app.stop_mysql()
|
||||
restart_mysql = True
|
||||
#rsync exiting data
|
||||
VolumeHelper.migrate_data(device_path, MYSQL_BASE_DIR)
|
||||
#mount the volume
|
||||
VolumeHelper.mount(device_path, mount_point)
|
||||
#TODO(cp16net) need to update the fstab here so that on a
|
||||
# restart the volume will be mounted automatically again
|
||||
LOG.debug(_("Mounted the volume."))
|
||||
#check mysql was installed and stopped
|
||||
if restart_mysql:
|
||||
app.start_mysql()
|
||||
app.install_and_secure(pkg, memory_mb)
|
||||
LOG.info("Creating initial databases and users following successful "
|
||||
"prepare.")
|
||||
@ -536,11 +560,12 @@ class MySqlApp(object):
|
||||
"""Prepares DBaaS on a Guest container."""
|
||||
|
||||
TIME_OUT = 1000
|
||||
MYSQL_PACKAGE_VERSION = "mysql-server-5.1"
|
||||
|
||||
def __init__(self, status):
|
||||
""" By default login with root no password for initial setup. """
|
||||
self.state_change_wait_time = config.Config.get(
|
||||
'state_change_wait_time', 2 * 60)
|
||||
self.state_change_wait_time = int(config.Config.get(
|
||||
'state_change_wait_time', 2 * 60))
|
||||
self.status = status
|
||||
|
||||
def _create_admin_user(self, client, password):
|
||||
@ -586,21 +611,21 @@ class MySqlApp(object):
|
||||
self._remove_remote_root_access(client)
|
||||
self._create_admin_user(client, admin_password)
|
||||
|
||||
self._internal_stop_mysql()
|
||||
self.stop_mysql()
|
||||
self._write_mycnf(pkg, memory_mb, admin_password)
|
||||
self._start_mysql()
|
||||
self.start_mysql()
|
||||
|
||||
self.status.end_install_or_restart()
|
||||
LOG.info(_("Dbaas preparation complete."))
|
||||
LOG.info(_("Dbaas install_and_secure complete."))
|
||||
|
||||
def _install_mysql(self, pkg):
|
||||
"""Install mysql server. The current version is 5.1"""
|
||||
LOG.debug(_("Installing mysql server"))
|
||||
pkg.pkg_install("mysql-server-5.1", self.TIME_OUT)
|
||||
pkg.pkg_install(self.MYSQL_PACKAGE_VERSION, self.TIME_OUT)
|
||||
LOG.debug(_("Finished installing mysql server"))
|
||||
#TODO(rnirmal): Add checks to make sure the package got installed
|
||||
|
||||
def _internal_stop_mysql(self, update_db=False):
|
||||
def stop_mysql(self, update_db=False):
|
||||
LOG.info(_("Stopping mysql..."))
|
||||
utils.execute_with_timeout("sudo", "/etc/init.d/mysql", "stop")
|
||||
if not self.status.wait_for_real_status_to_change_to(
|
||||
@ -623,8 +648,8 @@ class MySqlApp(object):
|
||||
def restart(self):
|
||||
try:
|
||||
self.status.begin_mysql_restart()
|
||||
self._internal_stop_mysql()
|
||||
self._start_mysql()
|
||||
self.stop_mysql()
|
||||
self.start_mysql()
|
||||
finally:
|
||||
self.status.end_install_or_restart()
|
||||
|
||||
@ -702,8 +727,7 @@ class MySqlApp(object):
|
||||
utils.execute_with_timeout("sudo", "ln", "-s", FINAL_MYCNF, ORIG_MYCNF)
|
||||
self.wipe_ib_logfiles()
|
||||
|
||||
|
||||
def _start_mysql(self, update_db=False):
|
||||
def start_mysql(self, update_db=False):
|
||||
LOG.info(_("Starting mysql..."))
|
||||
# This is the site of all the trouble in the restart tests.
|
||||
# Essentially what happens is thaty mysql start fails, but does not
|
||||
@ -736,8 +760,10 @@ class MySqlApp(object):
|
||||
"MySQL state == %s!") % self.status)
|
||||
raise RuntimeError("MySQL not stopped.")
|
||||
LOG.info(_("Initiating config."))
|
||||
self._write_mycnf(pkg, update_memory_mb, None)
|
||||
self._start_mysql(True)
|
||||
self._write_mycnf(pkg, updated_memory_mb, None)
|
||||
self.start_mysql(True)
|
||||
|
||||
def stop_mysql(self):
|
||||
self._internal_stop_mysql(True)
|
||||
def is_installed(self, pkg):
|
||||
#(cp16net) could raise an exception, does it need to be handled here?
|
||||
version = pkg.pkg_version(self.MYSQL_PACKAGE_VERSION)
|
||||
return not version is None
|
||||
|
@ -44,8 +44,9 @@ class GuestManager(service.Manager):
|
||||
"""Manages the tasks within a Guest VM."""
|
||||
|
||||
def __init__(self, guest_drivers=None, *args, **kwargs):
|
||||
service_type = CONFIG.get('service_type')
|
||||
try:
|
||||
service_impl = GUEST_SERVICES[CONFIG.get('service_type')]
|
||||
service_impl = GUEST_SERVICES[service_type]
|
||||
except KeyError as e:
|
||||
LOG.error(_("Could not create guest, no impl for key - %s") %
|
||||
service_type)
|
||||
|
@ -18,8 +18,10 @@
|
||||
"""
|
||||
Manages packages on the Guest VM.
|
||||
"""
|
||||
import commands
|
||||
import logging
|
||||
import pexpect
|
||||
import re
|
||||
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.common.exception import ProcessExecutionError
|
||||
@ -27,7 +29,6 @@ from reddwarf.common import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
# FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class PkgAdminLockError(exception.ReddwarfError):
|
||||
@ -157,7 +158,8 @@ class PkgAgent(object):
|
||||
def pkg_install(self, package_name, time_out):
|
||||
"""Installs a package."""
|
||||
try:
|
||||
utils.execute("apt-get", "update", run_as_root=True)
|
||||
utils.execute("apt-get", "update", run_as_root=True,
|
||||
root_helper="sudo")
|
||||
except ProcessExecutionError as e:
|
||||
LOG.error(_("Error updating the apt sources"))
|
||||
|
||||
@ -171,44 +173,45 @@ class PkgAgent(object):
|
||||
% package_name)
|
||||
|
||||
def pkg_version(self, package_name):
|
||||
"""Returns the installed version of the given package.
|
||||
|
||||
It is sometimes impossible to know if a package is completely
|
||||
unavailable before you attempt to install. Some packages may return
|
||||
no information from the dpkg command but then install fine with apt-get
|
||||
install.
|
||||
|
||||
"""
|
||||
child = pexpect.spawn("dpkg -l %s" % package_name)
|
||||
i = child.expect([".*No packages found matching*", "\+\+\+\-"])
|
||||
if i == 0:
|
||||
#raise PkgNotFoundError()
|
||||
cmd_list = ["dpkg", "-l", package_name]
|
||||
p = commands.getstatusoutput(' '.join(cmd_list))
|
||||
# check the command status code
|
||||
if not p[0] == 0:
|
||||
return None
|
||||
# Need to capture the version string
|
||||
child.expect("\n")
|
||||
i = child.expect(["<none>", ".*"])
|
||||
if i == 0:
|
||||
return None
|
||||
line = child.match.group()
|
||||
parts = line.split()
|
||||
# Should be something like:
|
||||
# ['un', 'cowsay', '<none>', '(no', 'description', 'available)']
|
||||
try:
|
||||
wait_and_close_proc(child)
|
||||
except pexpect.TIMEOUT:
|
||||
kill_proc(child)
|
||||
raise PkgTimeout("Remove process took too long.")
|
||||
if len(parts) <= 2:
|
||||
raise Error("Unexpected output.")
|
||||
if parts[1] != package_name:
|
||||
raise Error("Unexpected output:[1] == " + str(parts[1]))
|
||||
if parts[0] == 'un' or parts[2] == '<none>':
|
||||
return None
|
||||
return parts[2]
|
||||
# check the command output
|
||||
std_out = p[1]
|
||||
patterns = ['.*No packages found matching.*',
|
||||
"\w\w\s+(\S+)\s+(\S+)\s+(.*)$"]
|
||||
for line in std_out.split("\n"):
|
||||
for p in patterns:
|
||||
regex = re.compile(p)
|
||||
matches = regex.match(line)
|
||||
if matches:
|
||||
line = matches.group()
|
||||
parts = line.split()
|
||||
if not parts:
|
||||
msg = _("returned nothing")
|
||||
LOG.error(msg)
|
||||
raise exception.GuestError(msg)
|
||||
if len(parts) <= 2:
|
||||
msg = _("Unexpected output.")
|
||||
LOG.error(msg)
|
||||
raise exception.GuestError(msg)
|
||||
if parts[1] != package_name:
|
||||
msg = _("Unexpected output:[1] = %s" % str(parts[1]))
|
||||
LOG.error(msg)
|
||||
raise exception.GuestError(msg)
|
||||
if parts[0] == 'un' or parts[2] == '<none>':
|
||||
return None
|
||||
return parts[2]
|
||||
msg = _("version() saw unexpected output from dpkg!")
|
||||
LOG.error(msg)
|
||||
raise exception.GuestError(msg)
|
||||
|
||||
def pkg_remove(self, package_name, time_out):
|
||||
"""Removes a package."""
|
||||
if self.pkg_version(package_name) == None:
|
||||
if self.pkg_version(package_name) is None:
|
||||
return
|
||||
result = self._remove(package_name, time_out)
|
||||
|
||||
|
129
reddwarf/guestagent/volume.py
Normal file
129
reddwarf/guestagent/volume.py
Normal file
@ -0,0 +1,129 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 OpenStack, LLC.
|
||||
# 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.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pexpect
|
||||
|
||||
from reddwarf.common import config
|
||||
from reddwarf.common import utils
|
||||
from reddwarf.common.exception import GuestError
|
||||
from reddwarf.common.exception import ProcessExecutionError
|
||||
|
||||
TMP_MOUNT_POINT = "/mnt/volume"
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONFIG = config.Config
|
||||
|
||||
|
||||
class VolumeHelper(object):
|
||||
|
||||
@staticmethod
|
||||
def _has_volume_device(device_path):
|
||||
return not device_path is None
|
||||
|
||||
@staticmethod
|
||||
def migrate_data(device_path, mysql_base):
|
||||
""" Synchronize the data from the mysql directory to the new volume """
|
||||
utils.execute("sudo", "mkdir", "-p", TMP_MOUNT_POINT)
|
||||
VolumeHelper.mount(device_path, TMP_MOUNT_POINT)
|
||||
if not mysql_base[-1] == '/':
|
||||
mysql_base = "%s/" % mysql_base
|
||||
utils.execute("sudo", "rsync", "--safe-links", "--perms",
|
||||
"--recursive", "--owner", "--group", "--xattrs",
|
||||
"--sparse", mysql_base, TMP_MOUNT_POINT)
|
||||
VolumeHelper.unmount(device_path)
|
||||
|
||||
@staticmethod
|
||||
def _check_device_exists(device_path):
|
||||
"""Check that the device path exists.
|
||||
|
||||
Verify that the device path has actually been created and can report
|
||||
it's size, only then can it be available for formatting, retry
|
||||
num_tries to account for the time lag.
|
||||
"""
|
||||
try:
|
||||
num_tries = CONFIG.get('num_tries', 3)
|
||||
utils.execute('sudo', 'blockdev', '--getsize64', device_path,
|
||||
attempts=num_tries)
|
||||
except ProcessExecutionError:
|
||||
raise GuestError("InvalidDevicePath(path=%s)" % device_path)
|
||||
|
||||
@staticmethod
|
||||
def _check_format(device_path):
|
||||
"""Checks that an unmounted volume is formatted."""
|
||||
child = pexpect.spawn("sudo dumpe2fs %s" % device_path)
|
||||
try:
|
||||
i = child.expect(['has_journal', 'Wrong magic number'])
|
||||
if i == 0:
|
||||
return
|
||||
volume_fstype = CONFIG.get('volume_fstype', 'ext3')
|
||||
raise IOError('Device path at %s did not seem to be %s.' %
|
||||
(device_path, volume_fstype))
|
||||
except pexpect.EOF:
|
||||
raise IOError("Volume was not formatted.")
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
@staticmethod
|
||||
def _format(device_path):
|
||||
"""Calls mkfs to format the device at device_path."""
|
||||
volume_fstype = CONFIG.get('volume_fstype', 'ext3')
|
||||
format_options = CONFIG.get('format_options', '-m 5')
|
||||
cmd = "sudo mkfs -t %s %s %s" % (volume_fstype,
|
||||
format_options, device_path)
|
||||
volume_format_timeout = CONFIG.get('volume_format_timeout', 120)
|
||||
child = pexpect.spawn(cmd, timeout=volume_format_timeout)
|
||||
# child.expect("(y,n)")
|
||||
# child.sendline('y')
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
@staticmethod
|
||||
def format(device_path):
|
||||
"""Formats the device at device_path and checks the filesystem."""
|
||||
VolumeHelper._check_device_exists(device_path)
|
||||
VolumeHelper._format(device_path)
|
||||
VolumeHelper._check_format(device_path)
|
||||
|
||||
@staticmethod
|
||||
def mount(device_path, mount_point):
|
||||
if not os.path.exists(mount_point):
|
||||
os.makedirs(mount_point)
|
||||
volume_fstype = CONFIG.get('volume_fstype', 'ext3')
|
||||
mount_options = CONFIG.get('mount_options', 'noatime')
|
||||
cmd = "sudo mount -t %s -o %s %s %s" % (volume_fstype,
|
||||
mount_options,
|
||||
device_path, mount_point)
|
||||
child = pexpect.spawn(cmd)
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
@staticmethod
|
||||
def unmount(mount_point):
|
||||
if os.path.exists(mount_point):
|
||||
cmd = "sudo umount %s" % mount_point
|
||||
child = pexpect.spawn(cmd)
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
@staticmethod
|
||||
def resize_fs(device_path):
|
||||
"""Resize the filesystem on the specified device"""
|
||||
VolumeHelper._check_device_exists(device_path)
|
||||
try:
|
||||
utils.execute("sudo", "resize2fs", device_path)
|
||||
except ProcessExecutionError as err:
|
||||
LOG.error(err)
|
||||
raise GuestError("Error resizing the filesystem: %s"
|
||||
% device_path)
|
@ -23,19 +23,20 @@ import netaddr
|
||||
from reddwarf import db
|
||||
|
||||
from reddwarf.common import config
|
||||
#from reddwarf.guestagent import api as guest_api
|
||||
from reddwarf.common import exception as rd_exceptions
|
||||
from reddwarf.common import utils
|
||||
from reddwarf.instance.tasks import InstanceTask
|
||||
from reddwarf.instance.tasks import InstanceTasks
|
||||
from novaclient.v1_1.client import Client
|
||||
from reddwarf.common.models import ModelBase
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
from reddwarf.common.models import NovaRemoteModelBase
|
||||
from reddwarf.common.remote import create_nova_client
|
||||
from reddwarf.common.remote import create_nova_volume_client
|
||||
from reddwarf.common.remote import create_guest_client
|
||||
|
||||
|
||||
from eventlet import greenthread
|
||||
|
||||
|
||||
CONFIG = config.Config
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -45,12 +46,40 @@ def load_server(context, instance_id, server_id):
|
||||
client = create_nova_client(context)
|
||||
try:
|
||||
server = client.servers.get(server_id)
|
||||
volumes = load_volumes(context, server_id, client=client)
|
||||
except nova_exceptions.NotFound, e:
|
||||
LOG.debug("Could not find nova server_id(%s)" % server_id)
|
||||
raise rd_exceptions.ComputeInstanceNotFound(instance_id=instance_id,
|
||||
server_id=server_id)
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise rd_exceptions.ReddwarfError(str(e))
|
||||
return server
|
||||
return server, volumes
|
||||
|
||||
|
||||
def load_volumes(context, server_id, client=None):
|
||||
volume_support = config.Config.get("reddwarf_volume_support", 'False')
|
||||
if utils.bool_from_string(volume_support):
|
||||
if client is None:
|
||||
client = create_nova_client(context)
|
||||
volume_client = create_nova_volume_client(context)
|
||||
try:
|
||||
volumes = []
|
||||
if utils.bool_from_string(volume_support):
|
||||
volumes_info = client.volumes.get_server_volumes(server_id)
|
||||
volume_ids = [attachments.volumeId for attachments in
|
||||
volumes_info]
|
||||
for volume_id in volume_ids:
|
||||
volume_info = volume_client.volumes.get(volume_id)
|
||||
volume = {'id': volume_info.id,
|
||||
'size': volume_info.size}
|
||||
volumes.append(volume)
|
||||
except nova_exceptions.NotFound, e:
|
||||
LOG.debug("Could not find nova server_id(%s)" % server_id)
|
||||
raise rd_exceptions.VolumeAttachmentsNotFound(server_id=server_id)
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise rd_exceptions.ReddwarfError(str(e))
|
||||
return volumes
|
||||
return None
|
||||
|
||||
|
||||
# This probably should not happen here. Seems like it should
|
||||
@ -71,7 +100,7 @@ def populate_databases(dbs):
|
||||
databases.append(mydb.serialize())
|
||||
return databases
|
||||
except ValueError as ve:
|
||||
raise exception.BadRequest(ve.message)
|
||||
raise rd_exceptions.BadRequest(ve.message)
|
||||
|
||||
|
||||
class InstanceStatus(object):
|
||||
@ -95,13 +124,14 @@ VALID_ACTION_STATUSES = ["ACTIVE"]
|
||||
class Instance(object):
|
||||
|
||||
_data_fields = ['name', 'status', 'id', 'created', 'updated',
|
||||
'flavor', 'links', 'addresses']
|
||||
'flavor', 'links', 'addresses', 'volume']
|
||||
|
||||
def __init__(self, context, db_info, server, service_status):
|
||||
def __init__(self, context, db_info, server, service_status, volumes):
|
||||
self.context = context
|
||||
self.db_info = db_info
|
||||
self.server = server
|
||||
self.service_status = service_status
|
||||
self.volumes = volumes
|
||||
|
||||
@staticmethod
|
||||
def load(context, id):
|
||||
@ -113,11 +143,12 @@ class Instance(object):
|
||||
db_info = DBInstance.find_by(id=id)
|
||||
except rd_exceptions.NotFound:
|
||||
raise rd_exceptions.NotFound(uuid=id)
|
||||
server = load_server(context, db_info.id, db_info.compute_instance_id)
|
||||
server, volumes = load_server(context, db_info.id,
|
||||
db_info.compute_instance_id)
|
||||
task_status = db_info.task_status
|
||||
service_status = InstanceServiceStatus.find_by(instance_id=id)
|
||||
LOG.info("service status=%s" % service_status)
|
||||
return Instance(context, db_info, server, service_status)
|
||||
return Instance(context, db_info, server, service_status, volumes)
|
||||
|
||||
def delete(self, force=False):
|
||||
if not force and self.server.status in SERVER_INVALID_ACTION_STATUSES:
|
||||
@ -140,26 +171,94 @@ class Instance(object):
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise rd_exceptions.ReddwarfError()
|
||||
|
||||
@classmethod
|
||||
def _create_volume(cls, context, db_info, volume_size):
|
||||
volume_support = config.Config.get("reddwarf_volume_support", 'False')
|
||||
LOG.debug(_("Volume support = %s") % volume_support)
|
||||
if utils.bool_from_string(volume_support):
|
||||
LOG.debug(_("Starting to create the volume for the instance"))
|
||||
volume_client = create_nova_volume_client(context)
|
||||
volume_desc = ("mysql volume for %s" % context.tenant)
|
||||
volume_ref = volume_client.volumes.create(
|
||||
volume_size,
|
||||
display_name="mysql-%s" % db_info.id,
|
||||
display_description=volume_desc)
|
||||
#TODO(cp16net) this is bad to wait here for the volume create
|
||||
# before returning but this was a quick way to get it working
|
||||
# for now we need this to go into the task manager
|
||||
v_ref = volume_client.volumes.get(volume_ref.id)
|
||||
while not v_ref.status in ['available', 'error']:
|
||||
LOG.debug(_("waiting for volume [volume.status=%s]") %
|
||||
v_ref.status)
|
||||
greenthread.sleep(1)
|
||||
v_ref = volume_client.volumes.get(volume_ref.id)
|
||||
|
||||
if v_ref.status in ['error']:
|
||||
raise rd_exceptions.ReddwarfError(
|
||||
_("Could not create volume"))
|
||||
LOG.debug(_("Created volume %s") % v_ref)
|
||||
# The mapping is in the format:
|
||||
# <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>]
|
||||
# setting the delete_on_terminate instance to true=1
|
||||
mapping = "%s:%s:%s:%s" % (v_ref.id, '', v_ref.size, 1)
|
||||
# TODO(rnirmal) This mapping device needs to be configurable.
|
||||
# and we may have to do a little more trickery here.
|
||||
# We don't know what's the next device available on the
|
||||
# guest. Also in cases for ovz where this is mounted on
|
||||
# the host, that's not going to work for us.
|
||||
block_device = {'vdb': mapping}
|
||||
volume = [{'id': v_ref.id,
|
||||
'size': v_ref.size}]
|
||||
LOG.debug("block_device = %s" % block_device)
|
||||
LOG.debug("volume = %s" % volume)
|
||||
|
||||
device_path = CONFIG.get('device_path')
|
||||
mount_point = CONFIG.get('mount_point')
|
||||
LOG.debug(_("device_path = %s") % device_path)
|
||||
LOG.debug(_("mount_point = %s") % mount_point)
|
||||
else:
|
||||
LOG.debug(_("Skipping setting up the volume"))
|
||||
block_device = None
|
||||
device_path = None
|
||||
mount_point = None
|
||||
volume = None
|
||||
#end volume_support
|
||||
volume_info = {'block_device': block_device,
|
||||
'device_path': device_path,
|
||||
'mount_point': mount_point}
|
||||
return volume, volume_info
|
||||
|
||||
@classmethod
|
||||
def create(cls, context, name, flavor_ref, image_id,
|
||||
databases, service_type):
|
||||
databases, service_type, volume_size):
|
||||
db_info = DBInstance.create(name=name,
|
||||
task_status=InstanceTasks.NONE)
|
||||
LOG.debug(_("Created new Reddwarf instance %s...") % db_info.id)
|
||||
volume, volume_info = cls._create_volume(context,
|
||||
db_info,
|
||||
volume_size)
|
||||
client = create_nova_client(context)
|
||||
files = {"/etc/guest_info": "guest_id=%s\nservice_type=%s\n" %
|
||||
(db_info.id, service_type)}
|
||||
server = client.servers.create(name, image_id, flavor_ref,
|
||||
files=files)
|
||||
files=files,
|
||||
block_device_mapping=volume_info['block_device'])
|
||||
LOG.debug(_("Created new compute instance %s.") % server.id)
|
||||
|
||||
db_info.compute_instance_id = server.id
|
||||
db_info.save()
|
||||
service_status = InstanceServiceStatus.create(instance_id=db_info.id,
|
||||
status=ServiceStatuses.NEW)
|
||||
# Now wait for the response from the create to do additional work
|
||||
|
||||
guest = create_guest_client(context, db_info.id)
|
||||
guest.prepare(databases=[], memory_mb=512, users=[])
|
||||
return Instance(context, db_info, server, service_status)
|
||||
|
||||
# populate the databases
|
||||
model_schemas = populate_databases(databases)
|
||||
guest.prepare(512, model_schemas, users=[],
|
||||
device_path=volume_info['device_path'],
|
||||
mount_point=volume_info['mount_point'])
|
||||
return Instance(context, db_info, server, service_status, volume)
|
||||
|
||||
def get_guest(self):
|
||||
return create_guest_client(self.context, self.db_info.id)
|
||||
@ -201,8 +300,8 @@ class Instance(object):
|
||||
if self.server.status in ["ACTIVE", "SHUTDOWN"]:
|
||||
return InstanceStatus.SHUTDOWN
|
||||
else:
|
||||
LOG.error(_("While shutting down instance %s: server had status "
|
||||
" %s.") % (self.id, self.server.status))
|
||||
LOG.error(_("While shutting down instance (%s): server had "
|
||||
" status (%s).") % (self.id, self.server.status))
|
||||
return InstanceStatus.ERROR
|
||||
# For everything else we can look at the service status mapping.
|
||||
return self.service_status.status.api_status
|
||||
@ -311,7 +410,6 @@ class Instance(object):
|
||||
raise rd_exceptions.UnprocessableEntity(msg)
|
||||
|
||||
|
||||
|
||||
def create_server_list_matcher(server_list):
|
||||
# Returns a method which finds a server from the given list.
|
||||
def find_server(instance_id, server_id):
|
||||
@ -319,6 +417,9 @@ def create_server_list_matcher(server_list):
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
elif len(matches) < 1:
|
||||
# The instance was not found in the list and
|
||||
# this can happen if the instance is deleted from
|
||||
# nova but still in reddwarf database
|
||||
raise rd_exceptions.ComputeInstanceNotFound(
|
||||
instance_id=instance_id, server_id=server_id)
|
||||
else:
|
||||
@ -341,7 +442,8 @@ class Instances(object):
|
||||
ret = []
|
||||
find_server = create_server_list_matcher(servers)
|
||||
for db in db_infos:
|
||||
status = InstanceServiceStatus.find_by(instance_id=db.id)
|
||||
LOG.debug("checking for db [id=%s, compute_instance_id=%s]" %
|
||||
(db.id, db.compute_instance_id))
|
||||
try:
|
||||
# TODO(hub-cap): Figure out if this is actually correct.
|
||||
# We are not sure if we should be doing some validation.
|
||||
@ -350,11 +452,28 @@ class Instances(object):
|
||||
# nova db has compared to what we have. We should have
|
||||
# a way to handle this.
|
||||
server = find_server(db.id, db.compute_instance_id)
|
||||
volumes = load_volumes(context, db.compute_instance_id)
|
||||
status = InstanceServiceStatus.find_by(instance_id=db.id)
|
||||
LOG.info(_("Server api_status(%s)") %
|
||||
(status.status.api_status))
|
||||
|
||||
if not status.status:
|
||||
LOG.info(_("Server status could not be read for "
|
||||
"instance id(%s)") % (db.compute_instance_id))
|
||||
continue
|
||||
if status.status.api_status in ['SHUTDOWN']:
|
||||
LOG.info(_("Server was shutdown id(%s)") %
|
||||
(db.compute_instance_id))
|
||||
continue
|
||||
except rd_exceptions.ComputeInstanceNotFound:
|
||||
LOG.info(_("Could not find server %s") %
|
||||
db.compute_instance_id)
|
||||
continue
|
||||
ret.append(Instance(context, db, server, status))
|
||||
except ModelNotFoundError:
|
||||
LOG.info(_("Status entry not found either failed to start "
|
||||
"or instance was deleted"))
|
||||
continue
|
||||
ret.append(Instance(context, db, server, status, volumes))
|
||||
return ret
|
||||
|
||||
|
||||
@ -364,6 +483,7 @@ class DatabaseModelBase(ModelBase):
|
||||
@classmethod
|
||||
def create(cls, **values):
|
||||
values['id'] = utils.generate_uuid()
|
||||
values['created'] = utils.utcnow()
|
||||
instance = cls(**values).save()
|
||||
if not instance.is_valid():
|
||||
raise InvalidModelError(instance.errors)
|
||||
@ -372,10 +492,17 @@ class DatabaseModelBase(ModelBase):
|
||||
def save(self):
|
||||
if not self.is_valid():
|
||||
raise InvalidModelError(self.errors)
|
||||
self['updated_at'] = utils.utcnow()
|
||||
LOG.debug(_("Saving %s: %s") % (self.__class__.__name__, self.__dict__))
|
||||
self['updated'] = utils.utcnow()
|
||||
LOG.debug(_("Saving %s: %s") %
|
||||
(self.__class__.__name__, self.__dict__))
|
||||
return db.db_api.save(self)
|
||||
|
||||
def delete(self):
|
||||
self['updated'] = utils.utcnow()
|
||||
LOG.debug(_("Deleting %s: %s") %
|
||||
(self.__class__.__name__, self.__dict__))
|
||||
return db.db_api.delete(self)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.merge_attributes(kwargs)
|
||||
if not self.is_valid():
|
||||
|
@ -56,7 +56,10 @@ class BaseController(wsgi.Controller):
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.add_addresses = config.Config.get('add_addresses', False)
|
||||
self.add_addresses = utils.bool_from_string(
|
||||
config.Config.get('add_addresses', 'False'))
|
||||
self.add_volumes = utils.bool_from_string(
|
||||
config.Config.get('reddwarf_volume_support', 'False'))
|
||||
pass
|
||||
|
||||
def _extract_required_params(self, params, model_name):
|
||||
@ -166,7 +169,7 @@ class InstanceController(BaseController):
|
||||
def detail(self, req, tenant_id):
|
||||
"""Return all instances."""
|
||||
LOG.info(_("req : '%s'\n\n") % req)
|
||||
LOG.info(_("Detailing a database instance for tenant '%s'") % tenant_id)
|
||||
LOG.info(_("Detailing database instance for tenant '%s'") % tenant_id)
|
||||
#TODO(cp16net) return a detailed list instead of index
|
||||
return self.index(req, tenant_id, detailed=True)
|
||||
|
||||
@ -180,7 +183,8 @@ class InstanceController(BaseController):
|
||||
view_cls = views.InstancesDetailView if detailed \
|
||||
else views.InstancesView
|
||||
return wsgi.Result(view_cls(servers,
|
||||
add_addresses=self.add_addresses).data(), 200)
|
||||
add_addresses=self.add_addresses,
|
||||
add_volumes=self.add_volumes).data(), 200)
|
||||
|
||||
def show(self, req, tenant_id, id):
|
||||
"""Return a single instance."""
|
||||
@ -201,7 +205,8 @@ class InstanceController(BaseController):
|
||||
# Adding the root history, if it exists.
|
||||
history = models.RootHistory.load(context=context, instance_id=id)
|
||||
return wsgi.Result(views.InstanceDetailView(server, roothistory=history,
|
||||
add_addresses=self.add_addresses).data(), 200)
|
||||
add_addresses=self.add_addresses,
|
||||
add_volumes=self.add_volumes).data(), 200)
|
||||
|
||||
def delete(self, req, tenant_id, id):
|
||||
"""Delete a single instance."""
|
||||
@ -252,10 +257,13 @@ class InstanceController(BaseController):
|
||||
databases = body['instance'].get('databases')
|
||||
if databases is None:
|
||||
databases = []
|
||||
volume_size = body['instance']['volume']['size']
|
||||
instance = models.Instance.create(context, name, flavor_ref,
|
||||
image_id, databases, service_type)
|
||||
image_id, databases,
|
||||
service_type, volume_size)
|
||||
|
||||
return wsgi.Result(views.InstanceDetailView(instance).data(), 200)
|
||||
return wsgi.Result(views.InstanceDetailView(instance,
|
||||
add_volumes=self.add_volumes).data(), 200)
|
||||
|
||||
@staticmethod
|
||||
def _validate_body_not_empty(body):
|
||||
@ -295,12 +303,12 @@ class InstanceController(BaseController):
|
||||
body['instance']
|
||||
body['instance']['flavorRef']
|
||||
# TODO(cp16net) add in volume to the mix
|
||||
# volume_size = body['instance']['volume']['size']
|
||||
volume_size = body['instance']['volume']['size']
|
||||
except KeyError as e:
|
||||
LOG.error(_("Create Instance Required field(s) - %s") % e)
|
||||
raise rd_exceptions.ReddwarfError("Required element/key - %s "
|
||||
"was not specified" % e)
|
||||
# Instance._validate_volume_size(volume_size)
|
||||
InstanceController._validate_volume_size(volume_size)
|
||||
|
||||
@staticmethod
|
||||
def _validate_resize_instance(body):
|
||||
|
@ -15,6 +15,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_ip_address(addresses):
|
||||
if addresses is not None and \
|
||||
@ -23,14 +26,22 @@ def get_ip_address(addresses):
|
||||
return [addr.get('addr') for addr in addresses['private']]
|
||||
|
||||
|
||||
def get_volumes(volumes):
|
||||
LOG.debug("volumes - %s" % volumes)
|
||||
if volumes is not None and len(volumes) > 0:
|
||||
return {'size': volumes[0].get('size')}
|
||||
|
||||
|
||||
class InstanceView(object):
|
||||
|
||||
def __init__(self, instance, add_addresses=False):
|
||||
def __init__(self, instance, add_addresses=False, add_volumes=False):
|
||||
self.instance = instance
|
||||
self.add_addresses = add_addresses
|
||||
self.add_volumes = add_volumes
|
||||
|
||||
def data(self):
|
||||
ip = get_ip_address(self.instance.addresses)
|
||||
volumes = get_volumes(self.instance.volumes)
|
||||
instance_dict = {
|
||||
"id": self.instance.id,
|
||||
"name": self.instance.name,
|
||||
@ -39,6 +50,9 @@ class InstanceView(object):
|
||||
}
|
||||
if self.add_addresses and ip is not None and len(ip) > 0:
|
||||
instance_dict['ip'] = ip
|
||||
if self.add_volumes and volumes is not None:
|
||||
instance_dict['volume'] = volumes
|
||||
LOG.debug(instance_dict)
|
||||
return {"instance": instance_dict}
|
||||
|
||||
|
||||
@ -61,9 +75,10 @@ class InstanceDetailView(InstanceView):
|
||||
|
||||
class InstancesView(object):
|
||||
|
||||
def __init__(self, instances, add_addresses=False):
|
||||
def __init__(self, instances, add_addresses=False, add_volumes=False):
|
||||
self.instances = instances
|
||||
self.add_addresses = add_addresses
|
||||
self.add_volumes = add_volumes
|
||||
|
||||
def data(self):
|
||||
data = []
|
||||
@ -81,4 +96,5 @@ class InstancesDetailView(InstancesView):
|
||||
|
||||
def data_for_instance(self, instance):
|
||||
return InstanceDetailView(instance,
|
||||
self.add_addresses).data()['instance']
|
||||
self.add_addresses,
|
||||
self.add_volumes).data()['instance']
|
||||
|
@ -69,9 +69,11 @@ class FakeGuest(object):
|
||||
def list_users(self):
|
||||
return [self.users[name] for name in self.users]
|
||||
|
||||
def prepare(self, memory_mb, databases, users):
|
||||
def prepare(self, databases, memory_mb, users, device_path=None,
|
||||
mount_point=None):
|
||||
from reddwarf.instance.models import InstanceServiceStatus
|
||||
from reddwarf.instance.models import ServiceStatuses
|
||||
|
||||
def update_db():
|
||||
status = InstanceServiceStatus.find_by(instance_id=self.id)
|
||||
status.status = ServiceStatuses.RUNNING
|
||||
@ -87,6 +89,7 @@ class FakeGuest(object):
|
||||
def start_mysql_with_conf_changes(self, updated_memory_size):
|
||||
from reddwarf.instance.models import InstanceServiceStatus
|
||||
from reddwarf.instance.models import ServiceStatuses
|
||||
|
||||
def update_db():
|
||||
status = InstanceServiceStatus.find_by(instance_id=self.id)
|
||||
status.status = ServiceStatuses.RUNNING
|
||||
@ -96,6 +99,7 @@ class FakeGuest(object):
|
||||
def stop_mysql(self):
|
||||
from reddwarf.instance.models import InstanceServiceStatus
|
||||
from reddwarf.instance.models import ServiceStatuses
|
||||
|
||||
def update_db():
|
||||
status = InstanceServiceStatus.find_by(instance_id=self.id)
|
||||
status.status = ServiceStatuses.SHUTDOWN
|
||||
|
@ -96,7 +96,7 @@ class FakeServer(object):
|
||||
|
||||
@property
|
||||
def addresses(self):
|
||||
return {"private":[{"addr":"123.123.123.123"}]}
|
||||
return {"private": [{"addr":"123.123.123.123"}]}
|
||||
|
||||
def delete(self):
|
||||
self.schedule_status = []
|
||||
@ -194,3 +194,7 @@ class FakeClient(object):
|
||||
|
||||
def fake_create_nova_client(context):
|
||||
return FakeClient(context)
|
||||
|
||||
|
||||
def fake_create_nova_volume_client(context):
|
||||
return FakeClient(context)
|
||||
|
Loading…
Reference in New Issue
Block a user