Removing dependency on trove models in the guest agent

Reasons:
 - The guest agent is importing backup models and agent
   heartbeat, this triggers the all of the trove database
   setup logic which bloats the guest process on the host.

 - Moving the state to its own module and removing an unused
   function decreases the memory usage by about 15 - 20 megs.

Closes-Bug: #1375311
Change-Id: I126c0b89c170b325d85b3f09afca399b4f5de9e8
This commit is contained in:
Robert Myers
2014-09-27 19:25:59 -05:00
parent d8f0c4ab6d
commit 0007a5076a
14 changed files with 59 additions and 75 deletions

View File

@@ -17,6 +17,7 @@
from sqlalchemy import desc
from swiftclient.client import ClientException
from trove.backup.state import BackupState
from trove.common import cfg
from trove.common import exception
from trove.db.models import DatabaseModelBase
@@ -33,17 +34,6 @@ CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class BackupState(object):
NEW = "NEW"
BUILDING = "BUILDING"
SAVING = "SAVING"
COMPLETED = "COMPLETED"
FAILED = "FAILED"
DELETE_FAILED = "DELETE_FAILED"
RUNNING_STATES = [NEW, BUILDING, SAVING]
END_STATES = [COMPLETED, FAILED, DELETE_FAILED]
class Backup(object):
@classmethod

26
trove/backup/state.py Normal file
View File

@@ -0,0 +1,26 @@
# Copyright 2014 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.
#
class BackupState(object):
NEW = "NEW"
BUILDING = "BUILDING"
SAVING = "SAVING"
COMPLETED = "COMPLETED"
FAILED = "FAILED"
DELETE_FAILED = "DELETE_FAILED"
RUNNING_STATES = [NEW, BUILDING, SAVING]
END_STATES = [COMPLETED, FAILED, DELETE_FAILED]

View File

@@ -22,7 +22,6 @@ from eventlet import Timeout
from trove.common import cfg
from trove.common import exception
from trove.common import rpc as rd_rpc
from trove.guestagent import models as agent_models
from trove.openstack.common import rpc
from trove.openstack.common import log as logging
from trove.openstack.common.rpc import proxy
@@ -105,16 +104,6 @@ class API(proxy.RpcProxy):
"""Create the routing key based on the container id."""
return "guestagent.%s" % self.id
def _check_for_hearbeat(self):
"""Preemptively raise GuestTimeout if heartbeat is old."""
try:
agent = agent_models.AgentHeartBeat.find_by(instance_id=self.id)
if agent_models.AgentHeartBeat.is_active(agent):
return True
except exception.ModelNotFoundError as mnfe:
LOG.warn(mnfe)
raise exception.GuestTimeout()
def change_passwords(self, users):
"""Make an asynchronous call to change the passwords of one or more
users.
@@ -278,7 +267,6 @@ class API(proxy.RpcProxy):
def get_volume_info(self):
"""Make a synchronous call to get volume info for the container."""
LOG.debug("Check Volume Info on Instance %s.", self.id)
# self._check_for_hearbeat()
return self._call("get_filesystem_stats", AGENT_LOW_TIMEOUT,
fs_path=None)

View File

@@ -15,7 +15,7 @@
#
import logging
from trove.backup.models import BackupState
from trove.backup.state import BackupState
from trove.common import cfg
from trove.common import context as trove_context
from trove.conductor import api as conductor_api

View File

@@ -18,10 +18,9 @@ import time
from trove.common import cfg
from trove.common import context
from trove.common import instance as rd_instance
from trove.common import instance
from trove.conductor import api as conductor_api
from trove.guestagent.common import timeutils
from trove.instance import models as rd_models
from trove.openstack.common import log as logging
from trove.openstack.common.gettextutils import _
@@ -57,14 +56,12 @@ class BaseDbStatus(object):
def __init__(self):
if self._instance is not None:
raise RuntimeError("Cannot instantiate twice.")
self.status = rd_models.InstanceServiceStatus(
instance_id=CONF.guest_id,
status=rd_instance.ServiceStatuses.NEW)
self.status = None
self.restart_mode = False
def begin_install(self):
"""Called right before DB is prepared."""
self.set_status(rd_instance.ServiceStatuses.BUILDING)
self.set_status(instance.ServiceStatuses.BUILDING)
def begin_restart(self):
"""Called before restarting DB server."""
@@ -90,11 +87,10 @@ class BaseDbStatus(object):
True if DB app should be installed and attempts to ascertain
its status won't result in nonsense.
"""
return (self.status is not None and
self.status != rd_instance.ServiceStatuses.NEW and
self.status != rd_instance.ServiceStatuses.BUILDING and
self.status != rd_instance.ServiceStatuses.BUILD_PENDING and
self.status != rd_instance.ServiceStatuses.FAILED)
return (self.status != instance.ServiceStatuses.NEW and
self.status != instance.ServiceStatuses.BUILDING and
self.status != instance.ServiceStatuses.BUILD_PENDING and
self.status != instance.ServiceStatuses.FAILED)
@property
def _is_restarting(self):
@@ -104,7 +100,7 @@ class BaseDbStatus(object):
def is_running(self):
"""True if DB server is running."""
return (self.status is not None and
self.status == rd_instance.ServiceStatuses.RUNNING)
self.status == instance.ServiceStatuses.RUNNING)
def set_status(self, status):
"""Use conductor to update the DB app status."""

View File

@@ -23,8 +23,8 @@ from novaclient import exceptions as nova_exceptions
from trove.backup import models as bkup_models
from trove.backup.models import Backup
from trove.backup.models import BackupState
from trove.backup.models import DBBackup
from trove.backup.state import BackupState
from trove.cluster.models import Cluster
from trove.cluster.models import DBCluster
from trove.cluster import tasks

View File

@@ -15,6 +15,7 @@
from mox3 import mox
import trove.common.instance as tr_instance
from trove.backup import models as backup_models
from trove.backup import state
from trove.common.context import TroveContext
from trove.instance.tasks import InstanceTasks
from trove.instance import models as imodels
@@ -153,7 +154,7 @@ class RestartTaskStatusTests(MgmtInstanceBase):
name="forever_new",
description="forever new",
tenant_id=self.tenant_id,
state=backup_models.BackupState.NEW,
state=state.BackupState.NEW,
instance_id=self.db_info.id,
deleted=False)
@@ -161,7 +162,7 @@ class RestartTaskStatusTests(MgmtInstanceBase):
name="forever_build",
description="forever build",
tenant_id=self.tenant_id,
state=backup_models.BackupState.BUILDING,
state=state.BackupState.BUILDING,
instance_id=self.db_info.id,
deleted=False)
@@ -169,7 +170,7 @@ class RestartTaskStatusTests(MgmtInstanceBase):
name="forever_completed",
description="forever completed",
tenant_id=self.tenant_id,
state=backup_models.BackupState.COMPLETED,
state=state.BackupState.COMPLETED,
instance_id=self.db_info.id,
deleted=False)
@@ -190,9 +191,9 @@ class RestartTaskStatusTests(MgmtInstanceBase):
for backup in result:
if backup.name == 'forever_completed':
assert_equal(backup.status,
backup_models.BackupState.COMPLETED)
state.BackupState.COMPLETED)
else:
assert_equal(backup.status, backup_models.BackupState.FAILED)
assert_equal(backup.status, state.BackupState.FAILED)
@test(runs_after=[mgmt_reset_task_status_clears_backups])
def clear_test_backups(self):

View File

@@ -297,7 +297,8 @@ class FakeGuest(object):
return dbs
def create_backup(self, backup_info):
from trove.backup.models import Backup, BackupState
from trove.backup.models import Backup
from trove.backup.state import BackupState
backup = Backup.get_by_id(context=None,
backup_id=backup_info['id'])

View File

@@ -17,6 +17,7 @@ from mock import MagicMock, patch
import testtools
from trove.backup import models
from trove.backup import state
from trove.common import context
from trove.common import exception
from trove.common import utils
@@ -73,7 +74,7 @@ class BackupCreateTest(testtools.TestCase):
self.assertEqual(BACKUP_NAME, bu.name)
self.assertEqual(BACKUP_DESC, bu.description)
self.assertEqual(self.instance_id, bu.instance_id)
self.assertEqual(models.BackupState.NEW, bu.state)
self.assertEqual(state.BackupState.NEW, bu.state)
db_record = models.DBBackup.find_by(id=bu.id)
self.assertEqual(bu.id, db_record['id'])
@@ -81,7 +82,7 @@ class BackupCreateTest(testtools.TestCase):
self.assertEqual(BACKUP_DESC, db_record['description'])
self.assertEqual(self.instance_id,
db_record['instance_id'])
self.assertEqual(models.BackupState.NEW,
self.assertEqual(state.BackupState.NEW,
db_record['state'])
self.assertEqual(instance.datastore_version.id,
db_record['datastore_version_id'])
@@ -124,7 +125,7 @@ class BackupCreateTest(testtools.TestCase):
db_record['description'])
self.assertEqual(self.instance_id,
db_record['instance_id'])
self.assertEqual(models.BackupState.NEW,
self.assertEqual(state.BackupState.NEW,
db_record['state'])
self.assertEqual('parent_uuid',
db_record['parent_id'])
@@ -278,12 +279,12 @@ class BackupORMTest(testtools.TestCase):
self.assertTrue(self.backup.is_running)
def test_is_done(self):
self.backup.state = models.BackupState.COMPLETED
self.backup.state = state.BackupState.COMPLETED
self.backup.save()
self.assertTrue(self.backup.is_done)
def test_not_is_running(self):
self.backup.state = models.BackupState.COMPLETED
self.backup.state = state.BackupState.COMPLETED
self.backup.save()
self.assertFalse(self.backup.is_running)

View File

@@ -26,7 +26,7 @@ from trove.guestagent.common import operating_system
from trove.guestagent.strategies.backup import mysql_impl
from trove.guestagent.strategies.backup import couchbase_impl
from trove.guestagent.strategies.restore.base import RestoreRunner
from trove.backup.models import BackupState
from trove.backup.state import BackupState
from trove.guestagent.backup import backupagent
from trove.guestagent.strategies.backup.base import BackupRunner
from trove.guestagent.strategies.backup.base import UnknownBackupType

View File

@@ -14,6 +14,7 @@
import testtools
from trove.backup import models as bkup_models
from trove.backup import state
from trove.common import exception as t_exception
from trove.common import utils
from trove.common.instance import ServiceStatuses
@@ -58,7 +59,7 @@ class ConductorMethodTests(testtools.TestCase):
name=name,
description='This is a fake backup object.',
tenant_id=utils.generate_uuid(),
state=bkup_models.BackupState.NEW,
state=state.BackupState.NEW,
instance_id=self.instance_id)
backup.save()
return new_id

View File

@@ -73,27 +73,6 @@ class ApiTest(testtools.TestCase):
self.assertEqual('guestagent.' + self.FAKE_ID,
self.api._get_routing_key())
@mock.patch('trove.guestagent.models.AgentHeartBeat')
def test_check_for_heartbeat_positive(self, mock_agent):
self.assertTrue(self.api._check_for_hearbeat())
@mock.patch('trove.guestagent.models.AgentHeartBeat')
def test_check_for_heartbeat_exception(self, mock_agent):
# TODO(juice): maybe it would be ok to extend the test to validate
# the is_active method on the heartbeat
mock_agent.find_by.side_effect = exception.ModelNotFoundError("Uh Oh!")
# execute
self.assertRaises(exception.GuestTimeout, self.api._check_for_hearbeat)
# validate
self.assertEqual(mock_agent.is_active.call_count, 0)
@mock.patch('trove.guestagent.models.AgentHeartBeat')
def test_check_for_heartbeat_negative(self, mock_agent):
# TODO(juice): maybe it would be ok to extend the test to validate
# the is_active method on the heartbeat
mock_agent.is_active.return_value = False
self.assertRaises(exception.GuestTimeout, self.api._check_for_hearbeat)
def test_delete_queue(self):
trove_rpc.delete_queue = mock.Mock()
# execute

View File

@@ -1141,7 +1141,7 @@ class BaseDbStatusTest(testtools.TestCase):
self.baseDbStatus = BaseDbStatus()
self.baseDbStatus.status = None
self.assertFalse(self.baseDbStatus.is_installed)
self.assertTrue(self.baseDbStatus.is_installed)
def test_is_installed_building(self):
self.baseDbStatus = BaseDbStatus()

View File

@@ -28,6 +28,7 @@ import trove.db.models
from trove.taskmanager import models as taskmanager_models
import trove.guestagent.api
from trove.backup import models as backup_models
from trove.backup import state
from trove.common import remote
from trove.common.exception import GuestError
from trove.common.exception import PollTimeOut
@@ -595,7 +596,7 @@ class BackupTasksTest(testtools.TestCase):
self.backup.created = 'yesterday'
self.backup.updated = 'today'
self.backup.size = 2.0
self.backup.state = backup_models.BackupState.NEW
self.backup.state = state.BackupState.NEW
self.container_content = (None,
[{'name': 'first'},
{'name': 'second'},
@@ -636,7 +637,7 @@ class BackupTasksTest(testtools.TestCase):
'dummy context', self.backup.id)
self.assertFalse(backup_models.Backup.delete.called)
self.assertEqual(
backup_models.BackupState.DELETE_FAILED,
state.BackupState.DELETE_FAILED,
self.backup.state,
"backup should be in DELETE_FAILED status")
@@ -649,7 +650,7 @@ class BackupTasksTest(testtools.TestCase):
'dummy context', self.backup.id)
self.assertFalse(backup_models.Backup.delete.called)
self.assertEqual(
backup_models.BackupState.DELETE_FAILED,
state.BackupState.DELETE_FAILED,
self.backup.state,
"backup should be in DELETE_FAILED status")