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