From 15cfb4cd35fd1cf0c4cab8e6ef234f51a365ec27 Mon Sep 17 00:00:00 2001
From: Chris Behrens <cbehrens@codestud.com>
Date: Mon, 20 Jun 2011 16:26:57 +0000
Subject: [PATCH] Merged my 'create-num-instances' branch which adds support
 for --min_instances and --max_instances to zone-boot

Squashed commit of the following:

commit f8a9d157c1b6989ca61430b2829c69bafead9731
Author: Chris Behrens <cbehrens@codestud.com>
Date:   Mon Jun 20 16:24:26 2011 +0000

    updated tests for min_count/max_count

commit e093e9e883159d42a67ba8799f46fa0cdf333077
Author: Chris Behrens <cbehrens@codestud.com>
Date:   Mon Jun 20 16:13:16 2011 +0000

    adds --min_instances and --max_instances options to nova zone-boot
---
 novaclient/base.py    | 10 +++++++++-
 novaclient/servers.py | 10 ++++++++--
 novaclient/shell.py   | 31 +++++++++++++++++++++++++++----
 novaclient/zones.py   | 10 ++++++++--
 tests/fakeserver.py   |  4 ++--
 5 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/novaclient/base.py b/novaclient/base.py
index e6cce435a..3b97611d9 100644
--- a/novaclient/base.py
+++ b/novaclient/base.py
@@ -122,7 +122,8 @@ class BootingManagerWithFind(ManagerWithFind):
     """Like a `ManagerWithFind`, but has the ability to boot servers."""
     def _boot(self, resource_url, response_key, name, image, flavor,
               ipgroup=None, meta=None, files=None, zone_blob=None,
-              reservation_id=None, return_raw=False):
+              reservation_id=None, return_raw=False, min_count=None,
+              max_count=None):
         """
         Create (boot) a new server.
 
@@ -159,6 +160,13 @@ class BootingManagerWithFind(ManagerWithFind):
         if zone_blob:
             body["server"]["zone_blob"] = zone_blob
 
+        if not min_count:
+            min_count = 1
+        if not max_count:
+            max_count = min_count
+        body["server"]["min_count"] = min_count
+        body["server"]["max_count"] = max_count
+
         # Files are a slight bit tricky. They're passed in a "personality"
         # list to the POST. Each item is a dict giving a file name and the
         # base64-encoded contents of the file. We want to allow passing
diff --git a/novaclient/servers.py b/novaclient/servers.py
index 7032f189c..e87d7690d 100644
--- a/novaclient/servers.py
+++ b/novaclient/servers.py
@@ -222,7 +222,8 @@ class ServerManager(base.BootingManagerWithFind):
         return self._list("/servers%s%s" % (detail, reservation), "servers")
 
     def create(self, name, image, flavor, ipgroup=None, meta=None, files=None,
-               zone_blob=None, reservation_id=None):
+               zone_blob=None, reservation_id=None, min_count=None,
+               max_count=None):
         """
         Create (boot) a new server.
 
@@ -243,9 +244,14 @@ class ServerManager(base.BootingManagerWithFind):
                       this field.
         :param reservation_id: a UUID for the set of servers being requested.
         """
+        if not min_count:
+            min_count = 1
+        if not max_count:
+            max_count = min_count
         return self._boot("/servers", "server", name, image, flavor,
                           ipgroup=ipgroup, meta=meta, files=files,
-                          zone_blob=zone_blob, reservation_id=reservation_id)
+                          zone_blob=zone_blob, reservation_id=reservation_id,
+                          min_count=min_count, max_count=max_count)
 
     def update(self, server, name=None, password=None):
         """
diff --git a/novaclient/shell.py b/novaclient/shell.py
index f9722f009..0bd5ee971 100644
--- a/novaclient/shell.py
+++ b/novaclient/shell.py
@@ -229,6 +229,14 @@ class OpenStackShell(object):
 
     def _boot(self, args, reservation_id=None):
         """Boot a new server."""
+        min_count = args.min_instances
+        max_count = args.max_instances or min_count
+
+        if max_count > min_count:
+            raise CommandError("min_instances should be <= max_instances")
+        if not min_count or not max_count:
+            raise CommandError("min_instances nor max_instances should be 0")
+
         flavor = args.flavor or self.cs.flavors.find(ram=256)
         image = args.image or self.cs.images.find(name="Ubuntu 10.04 LTS "\
                                                        "(lucid)")
@@ -272,7 +280,7 @@ class OpenStackShell(object):
                 raise CommandError("Can't open '%s': %s" % (keyfile, e))
 
         return (args.name, image, flavor, ipgroup, metadata, files, 
-                reservation_id)
+                reservation_id, min_count, max_count)
 
     @arg('--flavor',
          default=None,
@@ -317,7 +325,9 @@ class OpenStackShell(object):
         server = self.cs.servers.create(args.name, image, flavor,
                                         ipgroup=ipgroup,
                                         meta=metadata,
-                                        files=files)
+                                        files=files,
+                                        min_count=min_count,
+                                        max_count=max_count)
         print_dict(server._info)
 
     @arg('--flavor',
@@ -408,11 +418,22 @@ class OpenStackShell(object):
          metavar='<reservation_id>',
          help="Reservation ID (a UUID). "\
               "If unspecified will be generated by the server.")
+    @arg('--min_instances',
+         default=1,
+         metavar='<number>',
+         help="The minimum number of instances to build. "\
+                 "Defaults to 1.")
+    @arg('--max_instances',
+         default=None,
+         metavar='<number>',
+         help="The maximum number of instances to build. "\
+                 "Defaults to 'min_instances' setting.")
     @arg('name', metavar='<name>', help='Name for the new server')
     def do_zone_boot(self, args):
         """Boot a new server, potentially across Zones."""
         reservation_id = args.reservation_id
-        name, image, flavor, ipgroup, metadata, files, reservation_id = \
+        name, image, flavor, ipgroup, metadata, \
+                files, reservation_id, min_count, max_count = \
                                  self._boot(args,
                                             reservation_id=reservation_id)
 
@@ -420,7 +441,9 @@ class OpenStackShell(object):
                                             ipgroup=ipgroup,
                                             meta=metadata,
                                             files=files,
-                                            reservation_id=reservation_id)
+                                            reservation_id=reservation_id,
+                                            min_count=min_count,
+                                            max_count=max_count)
         print "Reservation ID=", reservation_id
 
     def _translate_flavor_keys(self, collection):
diff --git a/novaclient/zones.py b/novaclient/zones.py
index 3fe79c417..ffc5b466e 100644
--- a/novaclient/zones.py
+++ b/novaclient/zones.py
@@ -107,7 +107,8 @@ class ZoneManager(base.BootingManagerWithFind):
         return self._create("/zones", body, "zone")
 
     def boot(self, name, image, flavor, ipgroup=None, meta=None, files=None,
-               zone_blob=None, reservation_id=None):
+               zone_blob=None, reservation_id=None, min_count=None,
+               max_count=None):
         """
         Create (boot) a new server while being aware of Zones.
 
@@ -128,10 +129,15 @@ class ZoneManager(base.BootingManagerWithFind):
                       this field.
         :param reservation_id: a UUID for the set of servers being requested.
         """
+        if not min_count:
+            min_count = 1
+        if not max_count:
+            max_count = min_count
         return self._boot("/zones/boot", "reservation_id", name, image, flavor,
                           ipgroup=ipgroup, meta=meta, files=files,
                           zone_blob=zone_blob, reservation_id=reservation_id,
-                          return_raw=True)
+                          return_raw=True, min_count=min_count,
+                          max_count=max_count)
 
     def select(self, *args, **kwargs):
         """
diff --git a/tests/fakeserver.py b/tests/fakeserver.py
index c4e60ce98..2a1f6dfbc 100644
--- a/tests/fakeserver.py
+++ b/tests/fakeserver.py
@@ -204,7 +204,7 @@ class FakeClient(OpenStackClient):
         assert_has_keys(body['server'],
                         required=['name', 'imageId', 'flavorId'],
                         optional=['sharedIpGroupId', 'metadata',
-                                                       'personality'])
+                                'personality', 'min_count', 'max_count'])
         if 'personality' in body['server']:
             for pfile in body['server']['personality']:
                 assert_has_keys(pfile, required=['path', 'contents'])
@@ -443,7 +443,7 @@ class FakeClient(OpenStackClient):
         assert_has_keys(body['server'],
                         required=['name', 'imageId', 'flavorId'],
                         optional=['sharedIpGroupId', 'metadata',
-                                                       'personality'])
+                                'personality', 'min_count', 'max_count'])
         if 'personality' in body['server']:
             for pfile in body['server']['personality']:
                 assert_has_keys(pfile, required=['path', 'contents'])