add ability for services to scale
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 19bcda9..8a10f37 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -29,6 +29,79 @@
 
     def __unicode__(self): return u'%s' % (self.name)
 
+    def get_scalable_nodes(self, slice, max_per_node=None, exclusive_slices=[]):
+        """
+             Get a list of nodes that can be used to scale up a slice.
+
+                slice - slice to scale up
+                max_per_node - maximum numbers of slivers that 'slice' can have on a single node
+                exclusive_slices - list of slices that must have no nodes in common with 'slice'.
+        """
+
+        from core.models import Node, Sliver # late import to get around order-of-imports constraint in __init__.py
+
+        nodes = list(Node.objects.all())
+
+        conflicting_slivers = Sliver.objects.filter(slice__in = exclusive_slices)
+        conflicting_nodes = Node.objects.filter(slivers__in = conflicting_slivers)
+
+        nodes = [x for x in nodes if x not in conflicting_nodes]
+
+        # If max_per_node is set, then limit the number of slivers this slice
+        # can have on a single node.
+        if max_per_node:
+            acceptable_nodes = []
+            for node in nodes:
+                existing_count = node.slivers.filter(slice=slice).count()
+                if existing_count < max_per_node:
+                    acceptable_nodes.append(node)
+            nodes = acceptable_nodes
+
+        return nodes
+
+    def pick_node(self, slice, max_per_node=None, exclusive_slices=[]):
+        # Pick the best node to scale up a slice.
+
+        nodes = self.get_scalable_nodes(slice, max_per_node, exclusive_slices)
+        nodes = sorted(nodes, key=lambda node: node.slivers.all().count())
+        if not nodes:
+            return None
+        return nodes[0]
+
+    def adjust_scale(self, slice_hint, scale, max_per_node=None, exclusive_slices=[]):
+        from core.models import Sliver # late import to get around order-of-imports constraint in __init__.py
+
+        slices = [x for x in self.slices.all() if slice_hint in x.name]
+        for slice in slices:
+            while slice.slivers.all().count() > scale:
+                s = slice.slivers.all()[0]
+                # print "drop sliver", s
+                s.delete()
+
+            while slice.slivers.all().count() < scale:
+                node = self.pick_node(slice, max_per_node, exclusive_slices)
+                if not node:
+                    # no more available nodes
+                    break
+
+                image = slice.default_image
+                if not image:
+                    raise XOSConfigurationError("No default_image for slice %s" % slice.name)
+
+                flavor = slice.default_flavor
+                if not flavor:
+                    raise XOSConfigurationError("No default_flavor for slice %s" % slice.name)
+
+                s = Sliver(slice=slice,
+                           node=node,
+                           creator=slice.creator,
+                           image=image,
+                           flavor=flavor,
+                           deployment=node.site_deployment.deployment)
+                s.save()
+
+                # print "add sliver", s
+
 class ServiceAttribute(PlCoreBase):
     name = models.SlugField(help_text="Attribute Name", max_length=128)
     value = StrippedCharField(help_text="Attribute Value", max_length=1024)