OpenStackProvider: Optionally wait for cloud-init
cloud-init writes a result.json when finished[1]. When wait_for_cloud_init=true is provided, this change uses result.json to detect when cloud-init has finished, and raises an exception if it finished with errors. [1] http://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/view/head:/doc/status.txt Change-Id: I7e0bcce733b008b488c8e1cf9bb74b5b6192cfbc
This commit is contained in:
@@ -14,8 +14,8 @@
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
|
||||
import novaclient.exceptions
|
||||
|
||||
@@ -46,6 +46,23 @@ def _get_address(s):
|
||||
raise RuntimeError("No address found for %s" % s)
|
||||
|
||||
|
||||
def _cloud_init_success(s):
|
||||
status, stdout, stderr = s.ssh.execute(
|
||||
"cat /run/cloud-init/result.json")
|
||||
if status:
|
||||
LOG.debug("Failed to read result.json on %s: %s" %
|
||||
(s, stderr))
|
||||
return False # Not finished (or no cloud-init)
|
||||
|
||||
res = json.loads(stdout)
|
||||
if res["v1"]["errors"]:
|
||||
raise RuntimeError("cloud-init exited with errors on %s: %s" %
|
||||
(s, res["v1"]["errors"]))
|
||||
|
||||
LOG.debug("cloud-init finished with no errors")
|
||||
return True # Success!
|
||||
|
||||
|
||||
@provider.configure(name="OpenStackProvider")
|
||||
class OpenStackProvider(provider.ProviderFactory):
|
||||
"""Provide VMs using an existing OpenStack cloud.
|
||||
@@ -84,6 +101,7 @@ class OpenStackProvider(provider.ProviderFactory):
|
||||
"region": {"type": "string"},
|
||||
"config_drive": {"type": "boolean"},
|
||||
"flavor_id": {"type": "string"},
|
||||
"wait_for_cloud_init": {"type": "boolean", "default": False},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -224,8 +242,10 @@ class OpenStackProvider(provider.ProviderFactory):
|
||||
for s in servers:
|
||||
s.ssh.wait(timeout=120, interval=5)
|
||||
|
||||
# NOTE(eyerediskin): usually ssh is ready much earlier then cloud-init
|
||||
time.sleep(8)
|
||||
if self.config.get("wait_for_cloud_init", False):
|
||||
for s in servers:
|
||||
utils.wait_for(s, is_ready=_cloud_init_success)
|
||||
|
||||
return servers
|
||||
|
||||
def destroy_servers(self):
|
||||
|
@@ -11,6 +11,7 @@
|
||||
"password": "admin",
|
||||
"auth_url": "http://example.net:5000/v2.0",
|
||||
"amount": 1,
|
||||
"wait_for_cloud_init": true,
|
||||
"image": {
|
||||
"checksum": "5101b2013b31d9f2f96f64f728926054",
|
||||
"name": "Ubuntu raring(added by rally)",
|
||||
|
@@ -15,6 +15,8 @@
|
||||
|
||||
"""Tests for OpenStack VM provider."""
|
||||
|
||||
import textwrap
|
||||
|
||||
import jsonschema
|
||||
import mock
|
||||
from oslotest import mockpatch
|
||||
@@ -154,6 +156,42 @@ class OpenStackProviderTestCase(test.TestCase):
|
||||
cfg["image"] = dict(checksum="checksum")
|
||||
OSProvider(mock.MagicMock(), cfg)
|
||||
|
||||
def test_cloud_init_success_notready(self):
|
||||
fake_server = mock.Mock()
|
||||
fake_server.ssh.execute.return_value = (1, "", "")
|
||||
|
||||
# Not ready yet -> False
|
||||
self.assertFalse(provider._cloud_init_success(fake_server))
|
||||
|
||||
def test_cloud_init_success_completed(self):
|
||||
fake_server = mock.Mock()
|
||||
result_json_text = textwrap.dedent("""
|
||||
{
|
||||
"v1": {
|
||||
"errors": [],
|
||||
"datasource": "DataSourceFoo"
|
||||
}
|
||||
}
|
||||
""")
|
||||
fake_server.ssh.execute.return_value = (0, result_json_text, "")
|
||||
# Completed (with no errors) -> True
|
||||
self.assertTrue(provider._cloud_init_success(fake_server))
|
||||
|
||||
def test_cloud_init_success_errors(self):
|
||||
fake_server = mock.Mock()
|
||||
result_json_text = textwrap.dedent("""
|
||||
{
|
||||
"v1": {
|
||||
"errors": ["omg!"],
|
||||
"datasource": "DataSourceFoo"
|
||||
}
|
||||
}
|
||||
""")
|
||||
fake_server.ssh.execute.return_value = (0, result_json_text, "")
|
||||
# Completed with errors -> Exception
|
||||
self.assertRaises(RuntimeError,
|
||||
provider._cloud_init_success, fake_server)
|
||||
|
||||
@mock.patch("time.sleep")
|
||||
@mock.patch(MOD_NAME + ".provider.Server")
|
||||
@mock.patch(MOD_NAME + ".osclients")
|
||||
|
Reference in New Issue
Block a user