Revision c744425f
b/doc/rapi.rst | ||
---|---|---|
328 | 328 |
|
329 | 329 |
``instance-create-reqv1`` |
330 | 330 |
Instance creation request data version 1 supported. |
331 |
``instance-reinstall-reqv1`` |
|
332 |
Instance reinstall supports body parameters. |
|
331 | 333 |
|
332 | 334 |
|
333 | 335 |
``/2/instances`` |
... | ... | |
570 | 572 |
``POST`` |
571 | 573 |
~~~~~~~~ |
572 | 574 |
|
573 |
Takes the parameters ``os`` (OS template name) and ``nostartup`` (bool). |
|
575 |
Returns a job ID. |
|
576 |
|
|
577 |
Body parameters: |
|
578 |
|
|
579 |
``os`` (string, required) |
|
580 |
Instance operating system. |
|
581 |
``start`` (bool, defaults to true) |
|
582 |
Whether to start instance after reinstallation. |
|
583 |
``osparams`` (dict) |
|
584 |
Dictionary with (temporary) OS parameters. |
|
585 |
|
|
586 |
For backwards compatbility, this resource also takes the query |
|
587 |
parameters ``os`` (OS template name) and ``nostartup`` (bool). New |
|
588 |
clients should use the body parameters. |
|
574 | 589 |
|
575 | 590 |
|
576 | 591 |
``/2/instances/[instance_name]/replace-disks`` |
b/lib/rapi/client.py | ||
---|---|---|
71 | 71 |
# Internal constants |
72 | 72 |
_REQ_DATA_VERSION_FIELD = "__version__" |
73 | 73 |
_INST_CREATE_REQV1 = "instance-create-reqv1" |
74 |
_INST_REINSTALL_REQV1 = "instance-reinstall-reqv1" |
|
74 | 75 |
_INST_NIC_PARAMS = frozenset(["mac", "ip", "mode", "link", "bridge"]) |
75 | 76 |
_INST_CREATE_V0_DISK_PARAMS = frozenset(["size"]) |
76 | 77 |
_INST_CREATE_V0_PARAMS = frozenset([ |
... | ... | |
858 | 859 |
("/%s/instances/%s/startup" % |
859 | 860 |
(GANETI_RAPI_VERSION, instance)), query, None) |
860 | 861 |
|
861 |
def ReinstallInstance(self, instance, os=None, no_startup=False): |
|
862 |
def ReinstallInstance(self, instance, os=None, no_startup=False, |
|
863 |
osparams=None): |
|
862 | 864 |
"""Reinstalls an instance. |
863 | 865 |
|
864 | 866 |
@type instance: str |
... | ... | |
870 | 872 |
@param no_startup: Whether to start the instance automatically |
871 | 873 |
|
872 | 874 |
""" |
875 |
if _INST_REINSTALL_REQV1 in self.GetFeatures(): |
|
876 |
body = { |
|
877 |
"start": not no_startup, |
|
878 |
} |
|
879 |
if os is not None: |
|
880 |
body["os"] = os |
|
881 |
if osparams is not None: |
|
882 |
body["osparams"] = osparams |
|
883 |
return self._SendRequest(HTTP_POST, |
|
884 |
("/%s/instances/%s/reinstall" % |
|
885 |
(GANETI_RAPI_VERSION, instance)), None, body) |
|
886 |
|
|
887 |
# Use old request format |
|
888 |
if osparams: |
|
889 |
raise GanetiApiError("Server does not support specifying OS parameters" |
|
890 |
" for instance reinstallation") |
|
891 |
|
|
873 | 892 |
query = [] |
874 | 893 |
if os: |
875 | 894 |
query.append(("os", os)) |
b/lib/rapi/rlib2.py | ||
---|---|---|
93 | 93 |
# Feature string for instance creation request data version 1 |
94 | 94 |
_INST_CREATE_REQV1 = "instance-create-reqv1" |
95 | 95 |
|
96 |
# Feature string for instance reinstall request version 1 |
|
97 |
_INST_REINSTALL_REQV1 = "instance-reinstall-reqv1" |
|
98 |
|
|
96 | 99 |
# Timeout for /2/jobs/[job_id]/wait. Gives job up to 10 seconds to change. |
97 | 100 |
_WFJC_TIMEOUT = 10 |
98 | 101 |
|
... | ... | |
134 | 137 |
"""Returns list of optional RAPI features implemented. |
135 | 138 |
|
136 | 139 |
""" |
137 |
return [_INST_CREATE_REQV1] |
|
140 |
return [_INST_CREATE_REQV1, _INST_REINSTALL_REQV1]
|
|
138 | 141 |
|
139 | 142 |
|
140 | 143 |
class R_2_os(baserlib.R_Generic): |
... | ... | |
838 | 841 |
return baserlib.SubmitJob([op]) |
839 | 842 |
|
840 | 843 |
|
844 |
def _ParseInstanceReinstallRequest(name, data): |
|
845 |
"""Parses a request for reinstalling an instance. |
|
846 |
|
|
847 |
""" |
|
848 |
if not isinstance(data, dict): |
|
849 |
raise http.HttpBadRequest("Invalid body contents, not a dictionary") |
|
850 |
|
|
851 |
ostype = baserlib.CheckParameter(data, "os") |
|
852 |
start = baserlib.CheckParameter(data, "start", exptype=bool, |
|
853 |
default=True) |
|
854 |
osparams = baserlib.CheckParameter(data, "osparams", default=None) |
|
855 |
|
|
856 |
ops = [ |
|
857 |
opcodes.OpShutdownInstance(instance_name=name), |
|
858 |
opcodes.OpReinstallInstance(instance_name=name, os_type=ostype, |
|
859 |
osparams=osparams), |
|
860 |
] |
|
861 |
|
|
862 |
if start: |
|
863 |
ops.append(opcodes.OpStartupInstance(instance_name=name, force=False)) |
|
864 |
|
|
865 |
return ops |
|
866 |
|
|
867 |
|
|
841 | 868 |
class R_2_instances_name_reinstall(baserlib.R_Generic): |
842 | 869 |
"""/2/instances/[instance_name]/reinstall resource. |
843 | 870 |
|
... | ... | |
852 | 879 |
automatically. |
853 | 880 |
|
854 | 881 |
""" |
855 |
instance_name = self.items[0] |
|
856 |
ostype = self._checkStringVariable('os') |
|
857 |
nostartup = self._checkIntVariable('nostartup') |
|
858 |
ops = [ |
|
859 |
opcodes.OpShutdownInstance(instance_name=instance_name), |
|
860 |
opcodes.OpReinstallInstance(instance_name=instance_name, os_type=ostype), |
|
861 |
] |
|
862 |
if not nostartup: |
|
863 |
ops.append(opcodes.OpStartupInstance(instance_name=instance_name, |
|
864 |
force=False)) |
|
882 |
if self.request_body: |
|
883 |
if self.queryargs: |
|
884 |
raise http.HttpBadRequest("Can't combine query and body parameters") |
|
885 |
|
|
886 |
body = self.request_body |
|
887 |
else: |
|
888 |
if not self.queryargs: |
|
889 |
raise http.HttpBadRequest("Missing query parameters") |
|
890 |
# Legacy interface, do not modify/extend |
|
891 |
body = { |
|
892 |
"os": self._checkStringVariable("os"), |
|
893 |
"start": not self._checkIntVariable("nostartup"), |
|
894 |
} |
|
895 |
|
|
896 |
ops = _ParseInstanceReinstallRequest(self.items[0], body) |
|
897 |
|
|
865 | 898 |
return baserlib.SubmitJob(ops) |
866 | 899 |
|
867 | 900 |
|
b/test/ganeti.rapi.client_unittest.py | ||
---|---|---|
131 | 131 |
self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON) |
132 | 132 |
self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION) |
133 | 133 |
self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1) |
134 |
self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1) |
|
134 | 135 |
self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS) |
135 | 136 |
|
136 | 137 |
|
... | ... | |
660 | 661 |
self.assertDryRun() |
661 | 662 |
|
662 | 663 |
def testReinstallInstance(self): |
664 |
self.rapi.AddResponse(serializer.DumpJson([])) |
|
663 | 665 |
self.rapi.AddResponse("19119") |
664 | 666 |
self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", |
665 | 667 |
os="DOS", |
... | ... | |
668 | 670 |
self.assertItems(["baz-instance"]) |
669 | 671 |
self.assertQuery("os", ["DOS"]) |
670 | 672 |
self.assertQuery("nostartup", ["1"]) |
673 |
self.assertEqual(self.rapi.CountPending(), 0) |
|
674 |
|
|
675 |
def testReinstallInstanceNew(self): |
|
676 |
self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1])) |
|
677 |
self.rapi.AddResponse("25689") |
|
678 |
self.assertEqual(25689, self.client.ReinstallInstance("moo-instance", |
|
679 |
os="Debian", |
|
680 |
no_startup=True)) |
|
681 |
self.assertHandler(rlib2.R_2_instances_name_reinstall) |
|
682 |
self.assertItems(["moo-instance"]) |
|
683 |
data = serializer.LoadJson(self.rapi.GetLastRequestData()) |
|
684 |
self.assertEqual(len(data), 2) |
|
685 |
self.assertEqual(data["os"], "Debian") |
|
686 |
self.assertEqual(data["start"], False) |
|
687 |
self.assertEqual(self.rapi.CountPending(), 0) |
|
688 |
|
|
689 |
def testReinstallInstanceWithOsparams1(self): |
|
690 |
self.rapi.AddResponse(serializer.DumpJson([])) |
|
691 |
self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance, |
|
692 |
"doo-instance", osparams={"x": "y"}) |
|
693 |
self.assertEqual(self.rapi.CountPending(), 0) |
|
694 |
|
|
695 |
def testReinstallInstanceWithOsparams2(self): |
|
696 |
osparams = { |
|
697 |
"Hello": "World", |
|
698 |
"foo": "bar", |
|
699 |
} |
|
700 |
self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1])) |
|
701 |
self.rapi.AddResponse("1717") |
|
702 |
self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance", |
|
703 |
osparams=osparams)) |
|
704 |
self.assertHandler(rlib2.R_2_instances_name_reinstall) |
|
705 |
self.assertItems(["zoo-instance"]) |
|
706 |
data = serializer.LoadJson(self.rapi.GetLastRequestData()) |
|
707 |
self.assertEqual(len(data), 2) |
|
708 |
self.assertEqual(data["osparams"], osparams) |
|
709 |
self.assertEqual(data["start"], True) |
|
710 |
self.assertEqual(self.rapi.CountPending(), 0) |
|
671 | 711 |
|
672 | 712 |
def testReplaceInstanceDisks(self): |
673 | 713 |
self.rapi.AddResponse("999") |
b/test/ganeti.rapi.rlib2_unittest.py | ||
---|---|---|
358 | 358 |
self.assertFalse(op.force_variant) |
359 | 359 |
|
360 | 360 |
|
361 |
class TestParseInstanceReinstallRequest(testutils.GanetiTestCase): |
|
362 |
def setUp(self): |
|
363 |
testutils.GanetiTestCase.setUp(self) |
|
364 |
|
|
365 |
self.Parse = rlib2._ParseInstanceReinstallRequest |
|
366 |
|
|
367 |
def _Check(self, ops, name): |
|
368 |
expcls = [ |
|
369 |
opcodes.OpShutdownInstance, |
|
370 |
opcodes.OpReinstallInstance, |
|
371 |
opcodes.OpStartupInstance, |
|
372 |
] |
|
373 |
|
|
374 |
self.assert_(compat.all(isinstance(op, exp) |
|
375 |
for op, exp in zip(ops, expcls))) |
|
376 |
self.assert_(compat.all(op.instance_name == name for op in ops)) |
|
377 |
|
|
378 |
def test(self): |
|
379 |
name = "shoo0tihohma" |
|
380 |
|
|
381 |
ops = self.Parse(name, {"os": "sys1", "start": True,}) |
|
382 |
self.assertEqual(len(ops), 3) |
|
383 |
self._Check(ops, name) |
|
384 |
self.assertEqual(ops[1].os_type, "sys1") |
|
385 |
self.assertFalse(ops[1].osparams) |
|
386 |
|
|
387 |
ops = self.Parse(name, {"os": "sys2", "start": False,}) |
|
388 |
self.assertEqual(len(ops), 2) |
|
389 |
self._Check(ops, name) |
|
390 |
self.assertEqual(ops[1].os_type, "sys2") |
|
391 |
|
|
392 |
osparams = { |
|
393 |
"reformat": "1", |
|
394 |
} |
|
395 |
ops = self.Parse(name, {"os": "sys4035", "start": True, |
|
396 |
"osparams": osparams,}) |
|
397 |
self.assertEqual(len(ops), 3) |
|
398 |
self._Check(ops, name) |
|
399 |
self.assertEqual(ops[1].os_type, "sys4035") |
|
400 |
self.assertEqual(ops[1].osparams, osparams) |
|
401 |
|
|
402 |
def testDefaults(self): |
|
403 |
name = "noolee0g" |
|
404 |
|
|
405 |
ops = self.Parse(name, {"os": "linux1"}) |
|
406 |
self.assertEqual(len(ops), 3) |
|
407 |
self._Check(ops, name) |
|
408 |
self.assertEqual(ops[1].os_type, "linux1") |
|
409 |
self.assertFalse(ops[1].osparams) |
|
410 |
|
|
411 |
|
|
361 | 412 |
if __name__ == '__main__': |
362 | 413 |
testutils.GanetiTestProgram() |
Also available in: Unified diff