Merge "Add cluster support for affinity/anti-affinity"

This commit is contained in:
Jenkins 2016-09-14 15:12:31 +00:00 committed by Gerrit Code Review
commit ce995f514a
5 changed files with 86 additions and 15 deletions

View File

@ -57,7 +57,7 @@ def cluster_delete(request, cluster_id):
def cluster_create(request, name, volume, flavor, num_instances,
datastore, datastore_version,
nics=None, root_password=None):
nics=None, root_password=None, locality=None):
instances = []
for i in range(num_instances):
instance = {}
@ -73,7 +73,8 @@ def cluster_create(request, name, volume, flavor, num_instances,
name,
datastore,
datastore_version,
instances=instances)
instances=instances,
locality=locality)
def cluster_grow(request, cluster_id, new_instances):

View File

@ -69,6 +69,15 @@ class LaunchForm(forms.SelfHandlingForm):
min_value=0,
initial=1,
help_text=_("Size of the volume in GB."))
locality = forms.ChoiceField(
label=_("Locality"),
choices=[("", "None"),
("affinity", "affinity"),
("anti-affinity", "anti-affinity")],
required=False,
help_text=_("Specify whether instances in the cluster will "
"be created on the same hypervisor (affinity) or on "
"different hypervisors (anti-affinity)."))
root_password = forms.CharField(
label=_("Root Password"),
required=False,
@ -158,6 +167,9 @@ class LaunchForm(forms.SelfHandlingForm):
msg = _("The number of shards must be greater than 1.")
self._errors["num_shards"] = self.error_class([msg])
if not self.data.get("locality", None):
self.cleaned_data["locality"] = None
return self.cleaned_data
@memoized.memoized_method
@ -280,6 +292,12 @@ class LaunchForm(forms.SelfHandlingForm):
if attr_key not in widget.attrs:
widget.attrs[attr_key] = field[1]
def _get_locality(self, data):
locality = None
if data.get('locality'):
locality = data['locality']
return locality
@sensitive_variables('data')
def handle(self, request, data):
try:
@ -295,8 +313,9 @@ class LaunchForm(forms.SelfHandlingForm):
LOG.info("Launching cluster with parameters "
"{name=%s, volume=%s, flavor=%s, "
"datastore=%s, datastore_version=%s",
"locality=%s",
data['name'], data['volume'], final_flavor,
datastore, datastore_version)
datastore, datastore_version, self._get_locality(data))
trove_api.trove.cluster_create(request,
data['name'],
@ -306,7 +325,8 @@ class LaunchForm(forms.SelfHandlingForm):
datastore=datastore,
datastore_version=datastore_version,
nics=data['network'],
root_password=root_password)
root_password=root_password,
locality=self._get_locality(data))
messages.success(request,
_('Launched cluster "%s"') % data['name'])
return True

View File

@ -16,6 +16,10 @@
<dd>{{ cluster.full_flavor.ram|mbformat }}</dd>
<dt>{% trans "Number of Instances" %}</dt>
<dd>{{ cluster.num_instances }}</dd>
{% if cluster.locality %}
<dt>{% trans "Locality" %}</dt>
<dd>{{ cluster.locality }}</dd>
{% endif %}
</dl>
{% block connection_info %}

View File

@ -78,7 +78,7 @@ class ClustersTests(test.TestCase):
'flavor_list')})
def test_index_pagination(self):
clusters = self.trove_clusters.list()
last_record = clusters[0]
last_record = clusters[1]
clusters = common.Paginated(clusters, next_marker="foo")
trove_api.trove.cluster_list(IsA(http.HttpRequest), marker=None)\
.AndReturn(clusters)
@ -235,7 +235,8 @@ class ClustersTests(test.TestCase):
datastore=cluster_datastore,
datastore_version=cluster_datastore_version,
nics=cluster_network,
root_password=None).AndReturn(self.trove_clusters.first())
root_password=None,
locality=None).AndReturn(self.trove_clusters.first())
self.mox.ReplayAll()
post = {
@ -291,7 +292,8 @@ class ClustersTests(test.TestCase):
datastore=cluster_datastore,
datastore_version=cluster_datastore_version,
nics=cluster_network,
root_password=None).AndReturn(self.trove_clusters.first())
root_password=None,
locality=None).AndReturn(self.trove_clusters.first())
self.mox.ReplayAll()
post = {
@ -344,7 +346,8 @@ class ClustersTests(test.TestCase):
datastore=cluster_datastore,
datastore_version=cluster_datastore_version,
nics=cluster_network,
root_password=None).AndReturn(self.trove_clusters.first())
root_password=None,
locality=None).AndReturn(self.trove_clusters.first())
self.mox.ReplayAll()
post = {
@ -380,6 +383,45 @@ class ClustersTests(test.TestCase):
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
self.assertContains(res, cluster.ip[0])
@test.create_stubs({trove_api.trove: ('cluster_get',
'instance_get',
'flavor_get',)})
def test_details_without_locality(self):
cluster = self.trove_clusters.list()[1]
trove_api.trove.cluster_get(IsA(http.HttpRequest), cluster.id) \
.MultipleTimes().AndReturn(cluster)
trove_api.trove.instance_get(IsA(http.HttpRequest), IsA(str)) \
.MultipleTimes().AndReturn(self.databases.first())
trove_api.trove.flavor_get(IsA(http.HttpRequest), IsA(str)) \
.MultipleTimes().AndReturn(self.flavors.first())
self.mox.ReplayAll()
details_url = reverse('horizon:project:database_clusters:detail',
args=[cluster.id])
res = self.client.get(details_url)
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
self.assertNotContains(res, "Locality")
@test.create_stubs({trove_api.trove: ('cluster_get',
'instance_get',
'flavor_get',)})
def test_details_with_locality(self):
cluster = self.trove_clusters.first()
trove_api.trove.cluster_get(IsA(http.HttpRequest), cluster.id) \
.MultipleTimes().AndReturn(cluster)
trove_api.trove.instance_get(IsA(http.HttpRequest), IsA(str)) \
.MultipleTimes().AndReturn(self.databases.first())
trove_api.trove.flavor_get(IsA(http.HttpRequest), IsA(str)) \
.MultipleTimes().AndReturn(self.flavors.first())
self.mox.ReplayAll()
details_url = reverse('horizon:project:database_clusters:detail',
args=[cluster.id])
res = self.client.get(details_url)
self.assertTemplateUsed(res, 'project/database_clusters/'
'_detail_overview.html')
self.assertContains(res, "Locality")
@test.create_stubs(
{trove_api.trove: ('cluster_get',
'cluster_grow'),

View File

@ -80,13 +80,14 @@ CLUSTER_DATA_ONE = {
],
"task": {
"name": "test_task"
}
},
"locality": "anti-affinity"
}
CLUSTER_DATA_TWO = {
"status": "ACTIVE",
"id": "dfbbd9ca-b5e1-4028-adb7-f78643e17998",
"name": "Test Cluster",
"id": "dfbbd9ca-b5e1-4028-adb7-f78199182122232",
"name": "Test Cluster2",
"created": "2014-04-25T20:19:23",
"updated": "2014-04-25T20:19:23",
"links": [],
@ -97,7 +98,7 @@ CLUSTER_DATA_TWO = {
"ip": ["10.0.0.1"],
"instances": [
{
"id": "416b0b16-ba55-4302-bbd3-ff566032e1c1",
"id": "416b0b16-ba55-4302-bbd3-ff8199182122232",
"name": "inst1",
"status": "ACTIVE",
"flavor": {
@ -109,7 +110,7 @@ CLUSTER_DATA_TWO = {
}
},
{
"id": "965ef811-7c1d-47fc-89f2-a89dfdd23ef2",
"id": "965ef811-7c1d-47fc-89f2-a88199182122232",
"name": "inst2",
"status": "ACTIVE",
"flavor": {
@ -121,7 +122,7 @@ CLUSTER_DATA_TWO = {
}
},
{
"id": "3642f41c-e8ad-4164-a089-3891bf7f2d2b",
"id": "3642f41c-e8ad-4164-a089-388199182122232",
"name": "inst3",
"status": "ACTIVE",
"flavor": {
@ -132,7 +133,10 @@ CLUSTER_DATA_TWO = {
"size": 100
}
}
]
],
"task": {
"name": "building"
},
}
DATABASE_DATA_ONE = {