Commit 48436b97 authored by Michael Hanselmann's avatar Michael Hanselmann

RAPI client: Implement old instance creation request format

Commit 8a47b447 implemented instance creation in the RAPI client,
but it left out support for the old instance creation request format.
This patch now implements the old format as good as possible. This
will only be used when talking to clusters before Ganeti 2.1.3.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 7be048f0
......@@ -60,6 +60,13 @@ NODE_ROLE_REGULAR = "regular"
# Internal constants
_REQ_DATA_VERSION_FIELD = "__version__"
_INST_CREATE_REQV1 = "instance-create-reqv1"
_INST_NIC_PARAMS = frozenset(["mac", "ip", "mode", "link", "bridge"])
_INST_CREATE_V0_DISK_PARAMS = frozenset(["size"])
_INST_CREATE_V0_PARAMS = frozenset([
"os", "pnode", "snode", "iallocator", "start", "ip_check", "name_check",
"hypervisor", "file_storage_dir", "file_driver", "dry_run",
])
_INST_CREATE_V0_DPARAMS = frozenset(["beparams", "hvparams"])
class Error(Exception):
......@@ -676,13 +683,82 @@ class GanetiRapiClient(object):
body.update((key, value) for key, value in kwargs.iteritems()
if key != "dry_run")
else:
# TODO: Implement instance creation request data version 0
# When implementing version 0, care should be taken to refuse unknown
# parameters and invalid values. The interface of this function must stay
# Old request format (version 0)
# The following code must make sure that an exception is raised when an
# unsupported setting is requested by the caller. Otherwise this can lead
# to bugs difficult to find. The interface of this function must stay
# exactly the same for version 0 and 1 (e.g. they aren't allowed to
# require different data types).
raise NotImplementedError("Support for instance creation request data"
" version 0 is not yet implemented")
# Validate disks
for idx, disk in enumerate(disks):
unsupported = set(disk.keys()) - _INST_CREATE_V0_DISK_PARAMS
if unsupported:
raise GanetiApiError("Server supports request version 0 only, but"
" disk %s specifies the unsupported parameters"
" %s, allowed are %s" %
(idx, unsupported,
list(_INST_CREATE_V0_DISK_PARAMS)))
assert (len(_INST_CREATE_V0_DISK_PARAMS) == 1 and
"size" in _INST_CREATE_V0_DISK_PARAMS)
disk_sizes = [disk["size"] for disk in disks]
# Validate NICs
if not nics:
raise GanetiApiError("Server supports request version 0 only, but"
" no NIC specified")
elif len(nics) > 1:
raise GanetiApiError("Server supports request version 0 only, but"
" more than one NIC specified")
assert len(nics) == 1
unsupported = set(nics[0].keys()) - _INST_NIC_PARAMS
if unsupported:
raise GanetiApiError("Server supports request version 0 only, but"
" NIC 0 specifies the unsupported parameters %s,"
" allowed are %s" %
(unsupported, list(_INST_NIC_PARAMS)))
# Validate other parameters
unsupported = (set(kwargs.keys()) - _INST_CREATE_V0_PARAMS -
_INST_CREATE_V0_DPARAMS)
if unsupported:
allowed = _INST_CREATE_V0_PARAMS.union(_INST_CREATE_V0_DPARAMS)
raise GanetiApiError("Server supports request version 0 only, but"
" the following unsupported parameters are"
" specified: %s, allowed are %s" %
(unsupported, list(allowed)))
# All required fields for request data version 0
body = {
_REQ_DATA_VERSION_FIELD: 0,
"name": name,
"disk_template": disk_template,
"disks": disk_sizes,
}
# NIC fields
assert len(nics) == 1
assert not (set(body.keys()) & set(nics[0].keys()))
body.update(nics[0])
# Copy supported fields
assert not (set(body.keys()) & set(kwargs.keys()))
body.update(dict((key, value) for key, value in kwargs.items()
if key in _INST_CREATE_V0_PARAMS))
# Merge dictionaries
for i in (value for key, value in kwargs.items()
if key in _INST_CREATE_V0_DPARAMS):
assert not (set(body.keys()) & set(i.keys()))
body.update(i)
assert not (set(kwargs.keys()) -
(_INST_CREATE_V0_PARAMS | _INST_CREATE_V0_DPARAMS))
assert not (set(body.keys()) & _INST_CREATE_V0_DPARAMS)
return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION,
query, body)
......
......@@ -26,6 +26,7 @@ import re
import unittest
import warnings
from ganeti import constants
from ganeti import http
from ganeti import serializer
......@@ -89,6 +90,9 @@ class RapiMock(object):
def AddResponse(self, response, code=200):
self._responses.insert(0, (code, response))
def CountPending(self):
return len(self._responses)
def GetLastHandler(self):
return self._last_handler
......@@ -111,6 +115,15 @@ class RapiMock(object):
return code, response
class TestConstants(unittest.TestCase):
def test(self):
self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
class RapiMockTest(unittest.TestCase):
def test(self):
rapi = RapiMock()
......@@ -196,6 +209,10 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
self.assertEqual(features, self.client.GetFeatures())
self.assertHandler(rlib2.R_2_features)
def testGetFeaturesNotFound(self):
self.rapi.AddResponse(None, code=404)
self.assertEqual([], self.client.GetFeatures())
def testGetOperatingSystems(self):
self.rapi.AddResponse("[\"beos\"]")
self.assertEqual(["beos"], self.client.GetOperatingSystems())
......@@ -259,10 +276,91 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
self.assertQuery("static", ["1"])
def testCreateInstanceOldVersion(self):
self.rapi.AddResponse(serializer.DumpJson([]))
self.assertRaises(NotImplementedError, self.client.CreateInstance,
"create", "inst1.example.com", "plain", [], [],
dry_run=True)
# No NICs
self.rapi.AddResponse(None, code=404)
self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
"create", "inst1.example.com", "plain", [], [])
self.assertEqual(self.rapi.CountPending(), 0)
# More than one NIC
self.rapi.AddResponse(None, code=404)
self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
"create", "inst1.example.com", "plain", [],
[{}, {}, {}])
self.assertEqual(self.rapi.CountPending(), 0)
# Unsupported NIC fields
self.rapi.AddResponse(None, code=404)
self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
"create", "inst1.example.com", "plain", [],
[{"x": True, "y": False}])
self.assertEqual(self.rapi.CountPending(), 0)
# Unsupported disk fields
self.rapi.AddResponse(None, code=404)
self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
"create", "inst1.example.com", "plain",
[{}, {"moo": "foo",}], [{}])
self.assertEqual(self.rapi.CountPending(), 0)
# Unsupported fields
self.rapi.AddResponse(None, code=404)
self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
"create", "inst1.example.com", "plain", [], [{}],
hello_world=123)
self.assertEqual(self.rapi.CountPending(), 0)
self.rapi.AddResponse(None, code=404)
self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
"create", "inst1.example.com", "plain", [], [{}],
memory=128)
self.assertEqual(self.rapi.CountPending(), 0)
# Normal creation
testnics = [
[{}],
[{ "mac": constants.VALUE_AUTO, }],
[{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
]
testdisks = [
[],
[{ "size": 128, }],
[{ "size": 321, }, { "size": 4096, }],
]
for idx, nics in enumerate(testnics):
for disks in testdisks:
beparams = {
constants.BE_MEMORY: 512,
constants.BE_AUTO_BALANCE: False,
}
hvparams = {
constants.HV_MIGRATION_PORT: 9876,
constants.HV_VNC_TLS: True,
}
self.rapi.AddResponse(None, code=404)
self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
job_id = self.client.CreateInstance("create", "inst1.example.com",
"plain", disks, nics,
pnode="node99", dry_run=True,
hvparams=hvparams,
beparams=beparams)
self.assertEqual(job_id, 3122617 + idx)
self.assertHandler(rlib2.R_2_instances)
self.assertDryRun()
self.assertEqual(self.rapi.CountPending(), 0)
data = serializer.LoadJson(self.http.last_request.data)
self.assertEqual(data["name"], "inst1.example.com")
self.assertEqual(data["disk_template"], "plain")
self.assertEqual(data["pnode"], "node99")
self.assertEqual(data[constants.BE_MEMORY], 512)
self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
self.assertEqual(data[constants.HV_VNC_TLS], True)
self.assertEqual(data["disks"], [disk["size"] for disk in disks])
def testCreateInstance(self):
self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment