summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYing Ruoyu <ruoyu.ying@intel.com>2018-03-15 06:33:03 -0400
committerDileep Ranganathan <dileep.ranganathan@intel.com>2018-03-28 05:53:16 -0700
commit04bf647bc948fed77d2574e0f97ffbab5f857522 (patch)
tree0004ca038db1fa856cef4ec597bdaac2b9a14fa7
parent39f63ef31ba3f0dc0db3ee8d5ff502a715702e1a (diff)
HPA Enablement for controller Translator Module
Add support for HPA constraints translation Add unit tests for HPA constraints validation Updated with mandatory and score parameters Updated flavorLabel and flavorProperties instead of label and features Change-Id: Iec8fff2ad71f8ebeb58ef807d8d2b95ed3635ec1 Issue-ID: OPTFRA-168 Signed-off-by: Ying Ruoyu <ruoyu.ying@intel.com>
-rw-r--r--conductor/conductor/controller/translator.py59
-rw-r--r--conductor/conductor/tests/unit/controller/test_translator.py257
2 files changed, 311 insertions, 5 deletions
diff --git a/conductor/conductor/controller/translator.py b/conductor/conductor/controller/translator.py
index 724b068..7bc212b 100644
--- a/conductor/conductor/controller/translator.py
+++ b/conductor/conductor/controller/translator.py
@@ -99,7 +99,16 @@ CONSTRAINTS = {
'required': ['controller'],
'optional': ['request'],
},
+ 'hpa': {
+ 'split': True,
+ 'required': ['evaluate'],
+ },
}
+HPA_FEATURES = ['architecture', 'hpa-feature', 'hpa-feature-attributes',
+ 'hpa-version', 'mandatory']
+HPA_OPTIONAL = ['score']
+HPA_ATTRIBUTES = ['hpa-attribute-key', 'hpa-attribute-value', 'operator',
+ 'unit']
class TranslatorException(Exception):
@@ -512,7 +521,7 @@ class Translator(object):
resolved_demands = \
response and response.get('resolved_demands')
- required_candidates = resolved_demands\
+ required_candidates = resolved_demands \
.get('required_candidates')
if not resolved_demands:
raise TranslatorException(
@@ -541,6 +550,51 @@ class Translator(object):
return parsed
+ def validate_hpa_constraints(self, req_prop, value):
+ for para in value.get(req_prop):
+ # Make sure there is at least one
+ # set of flavorLabel and flavorProperties
+ if not para.get('flavorLabel') \
+ or not para.get('flavorProperties') \
+ or para.get('flavorLabel') == '' \
+ or para.get('flavorProperties') == '':
+ raise TranslatorException(
+ "HPA requirements need at least "
+ "one set of flavorLabel and flavorProperties"
+ )
+ for feature in para.get('flavorProperties'):
+ if type(feature) is not dict:
+ raise TranslatorException("HPA feature must be a dict")
+ # process mandatory parameter
+ hpa_mandatory = set(HPA_FEATURES).difference(feature.keys())
+ if bool(hpa_mandatory):
+ raise TranslatorException(
+ "Lack of compulsory elements inside HPA feature")
+ # process optional parameter
+ hpa_optional = set(feature.keys()).difference(HPA_FEATURES)
+ if hpa_optional and not hpa_optional.issubset(HPA_OPTIONAL):
+ raise TranslatorException(
+ "Lack of compulsory elements inside HPA feature")
+ if feature.get('mandatory') == 'False' and not feature.get(
+ 'score'):
+ raise TranslatorException(
+ "Score needs to be present if mandatory is False")
+
+ for attr in feature.get('hpa-feature-attributes'):
+ if type(attr) is not dict:
+ raise TranslatorException(
+ "HPA feature attributes must be a dict")
+ for name in attr.keys():
+ if name not in HPA_ATTRIBUTES:
+ raise TranslatorException(
+ "Invalid attribute '{}' found inside HPA "
+ "feature attributes".format(name))
+ if list(attr.keys()) < ['hpa-attribute-key',
+ 'hpa-attribute-value', 'operator']:
+ raise TranslatorException(
+ "Lack of compulsory elements "
+ "inside HPA feature attributes")
+
def parse_constraints(self, constraints):
"""Validate/prepare constraints for use by the solver."""
if not isinstance(constraints, dict):
@@ -589,6 +643,9 @@ class Translator(object):
"No value specified for property '{}' in "
"constraint named '{}'".format(
req_prop, name))
+ # For HPA constraints
+ if constraint_type == 'hpa':
+ self.validate_hpa_constraints(req_prop, value)
# Make sure there are no unknown properties
optional = constraint_def.get('optional', [])
diff --git a/conductor/conductor/tests/unit/controller/test_translator.py b/conductor/conductor/tests/unit/controller/test_translator.py
index 0e3bf8e..3dedfdf 100644
--- a/conductor/conductor/tests/unit/controller/test_translator.py
+++ b/conductor/conductor/tests/unit/controller/test_translator.py
@@ -19,15 +19,15 @@
"""Test classes for translator"""
import os
-import yaml
-import uuid
import unittest
+import uuid
+import yaml
+from conductor import __file__ as conductor_root
from conductor.controller.translator import Translator
from conductor.controller.translator import TranslatorException
-from conductor import __file__ as conductor_root
-from oslo_config import cfg
from mock import patch
+from oslo_config import cfg
def get_template():
@@ -223,6 +223,255 @@ class TestNoExceptionTranslator(unittest.TestCase):
'type': 'distance_to_location'}}
self.assertEquals(self.Translator.parse_constraints(constraints), rtn)
+ def test_parse_hpa_constraints(self):
+ hpa_constraint = {
+ "hpa_constraint": {
+ "type": "hpa",
+ "demands": [
+ "vG"
+ ],
+ "properties": {
+ "evaluate": [
+ {'flavorLabel': 'xx',
+ 'flavorProperties': [{
+ 'hpa-feature': 'BasicCapabilities',
+ 'hpa-version': 'v1',
+ 'architecture': 'generic',
+ 'mandatory': 'False',
+ 'score': '5',
+ 'hpa-feature-attributes': [
+ {
+ 'hpa-attribute-key': 'numVirtualCpu',
+ 'hpa-attribute-value': '4',
+ 'operator': '='
+ },
+ {
+ 'hpa-attribute-key': 'virtualMemSize',
+ 'hpa-attribute-value': '4',
+ 'operator': '=',
+ 'unit': 'GB'
+ }
+ ]
+ }], }
+ ]
+ }}}
+ rtn = {
+ 'hpa_constraint_vG': {
+ 'demands': 'vG',
+ 'name': 'hpa_constraint',
+ 'properties': {'evaluate': [{
+ 'flavorProperties': [
+ {'architecture': 'generic',
+ 'mandatory': 'False',
+ 'score': '5',
+ 'hpa-feature': 'BasicCapabilities',
+ 'hpa-feature-attributes': [
+ {
+ 'hpa-attribute-key': 'numVirtualCpu',
+ 'hpa-attribute-value': '4',
+ 'operator': '='
+ },
+ {
+ 'hpa-attribute-key': 'virtualMemSize',
+ 'hpa-attribute-value': '4',
+ 'operator': '=',
+ 'unit': 'GB'
+ }
+ ],
+ 'hpa-version': 'v1'}],
+ 'flavorLabel': 'xx'}]},
+ 'type': 'hpa'
+ }
+ }
+
+ self.assertEquals(self.Translator.parse_constraints(hpa_constraint),
+ rtn)
+
+ hpa_constraint_2 = {
+ "hpa_constraint": {
+ "type": "hpa",
+ "demands": [
+ "vG"
+ ],
+ "properties": {
+ "evaluate": [
+ {'flavorLabel': 'xx',
+ 'flavorProperties': [{
+ 'hpa-feature': 'BasicCapabilities',
+ 'hpa-version': 'v1',
+ 'architecture': 'generic',
+ 'mandatory': 'True',
+ 'hpa-feature-attributes': [
+ {
+ 'hpa-attribute-key': 'numVirtualCpu',
+ 'hpa-attribute-value': '4',
+ 'operator': '='
+ },
+ {
+ 'hpa-attribute-key': 'virtualMemSize',
+ 'hpa-attribute-value': '4',
+ 'operator': '=',
+ 'unit': 'GB'
+ }
+ ]
+ }], }
+ ]
+ }}}
+ rtn_2 = {
+ 'hpa_constraint_vG': {
+ 'demands': 'vG',
+ 'name': 'hpa_constraint',
+ 'properties': {'evaluate': [{
+ 'flavorProperties': [
+ {'architecture': 'generic',
+ 'mandatory': 'True',
+ 'hpa-feature': 'BasicCapabilities',
+ 'hpa-feature-attributes': [
+ {
+ 'hpa-attribute-key': 'numVirtualCpu',
+ 'hpa-attribute-value': '4',
+ 'operator': '='
+ },
+ {
+ 'hpa-attribute-key': 'virtualMemSize',
+ 'hpa-attribute-value': '4',
+ 'operator': '=',
+ 'unit': 'GB'
+ }
+ ],
+ 'hpa-version': 'v1'}],
+ 'flavorLabel': 'xx'}]},
+ 'type': 'hpa'
+ }
+ }
+
+ self.assertEquals(self.Translator.parse_constraints(hpa_constraint_2),
+ rtn_2)
+
+ def test_parse_hpa_constraints_format_validation(self):
+ hpa_constraint_1 = {
+ "hpa_constraint": {
+ "type": "hpa",
+ "demands": [
+ "vG"
+ ],
+ "properties": {
+ "evaluate": [{'flavor': 'xx',
+ 'flavorProperties': []}]
+ }
+ }
+ }
+ hpa_constraint_2 = {
+ "hpa_constraint": {
+ "type": "hpa",
+ "demands": [
+ "vG"
+ ],
+ "properties": {
+ "evaluate": [
+ {'flavorLabel': 'xx',
+ 'flavorProperties': [
+ {
+ 'hpa-feature': '',
+ 'hpa-version': '',
+ 'architecture': '',
+ 'mandatory': '',
+ 'hpa-feature-attributes': [''],
+ }
+ ]}
+ ]
+ }
+ }
+ }
+
+ hpa_constraint_3 = {
+ "hpa_constraint": {
+ "type": "hpa",
+ "demands": [
+ "vG"
+ ],
+ "properties": {
+ "evaluate": [
+ {'flavorLabel': 'xx',
+ 'flavorProperties': [
+ {
+ 'hpa-feature': 'BasicCapabilities',
+ 'hpa-version': 'v1',
+ 'architecture': 'generic',
+ 'mandatory': 'False',
+ 'score': '5',
+ 'hpa-feature-attributes': [
+ {
+ 'hpa-attribute-key': 'numVirtualCpu',
+ 'hpa-attribute-value': '4',
+
+ },
+ ]
+ }
+ ], }
+ ]
+ }
+ }
+ }
+
+ hpa_constraint_4 = {
+ "hpa_constraint": {
+ "type": "hpa",
+ "demands": [
+ "vG"
+ ],
+ "properties": {
+ "evaluate": [{'flavorLabel': 'xx',
+ 'flavorProperties': [{
+ 'hpa-feature': '',
+ 'architecture': '',
+ 'mandatory': '',
+ 'hpa-feature-attributes': [''],
+ }]}]
+ }
+ }
+ }
+
+ hpa_constraint_5 = {
+ "hpa_constraint": {
+ "type": "hpa",
+ "demands": [
+ "vG"
+ ],
+ "properties": {
+ "evaluate": [
+ {'flavorLabel': 'xx',
+ 'flavorProperties': [
+ {
+ 'hpa-feature': 'BasicCapabilities',
+ 'hpa-version': 'v1',
+ 'architecture': 'generic',
+ 'mandatory': 'False',
+ 'hpa-feature-attributes': [
+ {
+ 'hpa-attribute-key': 'numVirtualCpu',
+ 'hpa-attribute-value': '4',
+
+ },
+ ]
+ }
+ ], }
+ ]
+ }
+ }
+ }
+
+ self.assertRaises(TranslatorException,
+ self.Translator.parse_constraints, hpa_constraint_1)
+ self.assertRaises(TranslatorException,
+ self.Translator.parse_constraints, hpa_constraint_2)
+ self.assertRaises(TranslatorException,
+ self.Translator.parse_constraints, hpa_constraint_3)
+ self.assertRaises(TranslatorException,
+ self.Translator.parse_constraints, hpa_constraint_4)
+ self.assertRaises(TranslatorException,
+ self.Translator.parse_constraints, hpa_constraint_5)
+
def test_parse_vim_fit_constraint(self):
vim_fit_constraint = {
"check_cloud_capacity": {