Merge "Add cluster support for affinity/anti-affinity"
This commit is contained in:
commit
ce995f514a
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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 %}
|
||||
|
@ -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'),
|
||||
|
@ -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 = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user