Merge "Remove fuel deployment engine"
This commit is contained in:
commit
a662609f55
@ -1,214 +0,0 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common import objects
|
||||
from rally import consts
|
||||
from rally.deployment import engine
|
||||
from rally.deployment.fuel import fuelclient
|
||||
from rally import exceptions
|
||||
|
||||
|
||||
FILTER_SCHEMA = {
|
||||
"type": "string",
|
||||
"pattern": "^(ram|cpus|storage|mac)(==|<=?|>=?|!=)(.+)$",
|
||||
}
|
||||
|
||||
NODE_SCHEMA = {
|
||||
"type": "object",
|
||||
"required": ["amount"],
|
||||
"properties": {
|
||||
"amount": {"type": "integer"},
|
||||
"filters": {
|
||||
"type": "array",
|
||||
"uniqueItems": True,
|
||||
"items": FILTER_SCHEMA,
|
||||
},
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
|
||||
IPV4_PATTERN = "(\d+\.){3}\d+"
|
||||
IPV4_ADDRESS_PATTERN = "^%s$" % IPV4_PATTERN
|
||||
IPV4_CIDR_PATTERN = "^%s\/\d+$" % IPV4_PATTERN
|
||||
|
||||
IP_RANGE_SCHEMA = {
|
||||
"type": "array",
|
||||
"maxItems": 2,
|
||||
"minItems": 2,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": IPV4_ADDRESS_PATTERN,
|
||||
}
|
||||
}
|
||||
|
||||
NETWORK_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cidr": {"type": "string", "pattern": IPV4_CIDR_PATTERN},
|
||||
"gateway": {"type": "string", "pattern": IPV4_ADDRESS_PATTERN},
|
||||
"ip_ranges": {"type": "array", "items": IP_RANGE_SCHEMA},
|
||||
"vlan_start": {"type": "integer"},
|
||||
}
|
||||
}
|
||||
|
||||
NETWORKS_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"public": NETWORK_SCHEMA,
|
||||
"floating": NETWORK_SCHEMA,
|
||||
"management": NETWORK_SCHEMA,
|
||||
"storage": NETWORK_SCHEMA,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@engine.configure(name="FuelEngine")
|
||||
class FuelEngine(engine.Engine):
|
||||
"""Deploy with FuelWeb.
|
||||
|
||||
Sample configuration:
|
||||
|
||||
{
|
||||
"type": "FuelEngine",
|
||||
"deploy_name": "Rally multinode 01",
|
||||
"release": "Havana on CentOS 6.4",
|
||||
"api_url": "http://10.20.0.2:8000/api/v1/",
|
||||
"mode": "multinode",
|
||||
"nodes": {
|
||||
"controller": {"amount": 1, "filters": ["storage>80G"]},
|
||||
"compute": {"amount": 1, "filters": ["storage>80G"]}
|
||||
},
|
||||
"net_provider": "nova_network",
|
||||
"dns_nameservers": ["172.18.208.44", "8.8.8.8"],
|
||||
"networks": {
|
||||
|
||||
"public": {
|
||||
"cidr": "10.3.3.0/24",
|
||||
"gateway": "10.3.3.1",
|
||||
"ip_ranges": [["10.3.3.5", "10.3.3.254"]],
|
||||
"vlan_start": 14
|
||||
},
|
||||
|
||||
"floating": {
|
||||
"cidr": "10.3.4.0/24",
|
||||
"ip_ranges": [["10.3.4.5", "10.3.4.254"]],
|
||||
"vlan_start": 14
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"required": ["deploy_name", "api_url", "mode", "networks",
|
||||
"nodes", "release", "net_provider"],
|
||||
"properties": {
|
||||
"release": {"type": "string"},
|
||||
"deploy_name": {"type": "string"},
|
||||
"api_url": {"type": "string"},
|
||||
"mode": {"type": "string"},
|
||||
"net_provider": {"type": "string"},
|
||||
"networks": NETWORKS_SCHEMA,
|
||||
"nodes": {
|
||||
"type": "object",
|
||||
"required": ["controller"],
|
||||
"properties": {
|
||||
"controller": NODE_SCHEMA,
|
||||
"compute": NODE_SCHEMA,
|
||||
"cinder": NODE_SCHEMA,
|
||||
"cinder+compute": NODE_SCHEMA,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def validate(self):
|
||||
super(FuelEngine, self).validate()
|
||||
if "compute" not in self.config["nodes"]:
|
||||
if "cinder+compute" not in self.config["nodes"]:
|
||||
raise exceptions.ValidationError(
|
||||
_("At least one compute is required."))
|
||||
|
||||
def _get_nodes(self, key):
|
||||
if key not in self.config["nodes"]:
|
||||
return []
|
||||
amount = self.config["nodes"][key]["amount"]
|
||||
filters = self.config["nodes"][key]["filters"]
|
||||
nodes = []
|
||||
for i in range(amount):
|
||||
node = self.nodes.pop(filters)
|
||||
if node is None:
|
||||
raise exceptions.NoNodesFound(filters=filters)
|
||||
nodes.append(node)
|
||||
return nodes
|
||||
|
||||
def _get_release_id(self):
|
||||
releases = self.client.get_releases()
|
||||
for release in releases:
|
||||
if release["name"] == self.config["release"]:
|
||||
return release["id"]
|
||||
raise exceptions.UnknownRelease(release=self.config["release"])
|
||||
|
||||
def deploy(self):
|
||||
self.client = fuelclient.FuelClient(self.config["api_url"])
|
||||
|
||||
self.nodes = self.client.get_nodes()
|
||||
|
||||
controllers = self._get_nodes("controller")
|
||||
computes = self._get_nodes("compute")
|
||||
cinders = self._get_nodes("cinder")
|
||||
computes_cinders = self._get_nodes("cinder+compute")
|
||||
|
||||
cluster = fuelclient.FuelCluster(
|
||||
self.client,
|
||||
name=self.config["deploy_name"],
|
||||
release=self._get_release_id(),
|
||||
mode=self.config["mode"],
|
||||
net_provider=self.config["net_provider"],
|
||||
net_segment_type=self.config.get("net_segment_type", "gre"),
|
||||
)
|
||||
|
||||
cluster.set_nodes(controllers, ["controller"])
|
||||
cluster.set_nodes(computes, ["compute"])
|
||||
cluster.set_nodes(cinders, ["cinder"])
|
||||
cluster.set_nodes(computes_cinders, ["compute", "cinder"])
|
||||
|
||||
cluster.configure_network(self.config["networks"])
|
||||
cluster.deploy()
|
||||
|
||||
self.deployment.add_resource("FuelEngine",
|
||||
type="cloud",
|
||||
info={"id": cluster.cluster["id"]})
|
||||
|
||||
ip = cluster.get_endpoint_ip()
|
||||
attrs = cluster.get_attributes()["editable"]["access"]
|
||||
|
||||
admin_credential = objects.Credential(
|
||||
"http://%s:5000/v2.0/" % ip,
|
||||
attrs["user"]["value"],
|
||||
attrs["password"]["value"],
|
||||
attrs["tenant"]["value"],
|
||||
consts.EndpointPermission.ADMIN)
|
||||
return {"admin": admin_credential}
|
||||
|
||||
def cleanup(self):
|
||||
resources = self.deployment.get_resources(provider_name="FuelEngine",
|
||||
type="cloud")
|
||||
self.client = fuelclient.FuelClient(self.config["api_url"])
|
||||
for res in resources:
|
||||
self.client.delete_cluster(res["info"]["id"])
|
||||
objects.Deployment.delete_resource(res["id"])
|
@ -1,264 +0,0 @@
|
||||
# Copyright 2013: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
from rally.common import logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
FILTER_REG = re.compile(r"^([a-z]+)\s*([<>=!]=|<|>)\s*(.+)$")
|
||||
INT_REG = re.compile(r"^(\d+)(K|M|G|T)?$")
|
||||
|
||||
|
||||
class FuelException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class FuelClientException(FuelException):
|
||||
|
||||
def __init__(self, code, body):
|
||||
self.code = code
|
||||
self.body = body
|
||||
|
||||
def __str__(self):
|
||||
return ("FuelClientException. "
|
||||
"Code: %(code)d Body: %(body)s" % {"code": self.code,
|
||||
"body": self.body})
|
||||
|
||||
|
||||
class FuelNetworkVerificationFailed(FuelException):
|
||||
pass
|
||||
|
||||
|
||||
class FuelNode(object):
|
||||
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.ATTRIBUTE_MAP = {
|
||||
"==": lambda x, y: x == y,
|
||||
"!=": lambda x, y: x != y,
|
||||
"<=": lambda x, y: x <= y,
|
||||
">=": lambda x, y: x >= y,
|
||||
"<": lambda x, y: x < y,
|
||||
">": lambda x, y: x > y,
|
||||
}
|
||||
self.FACTOR_MAP = {
|
||||
"K": 1024,
|
||||
"M": 1048576,
|
||||
"G": 1073741824,
|
||||
"T": 1099511627776,
|
||||
None: 1,
|
||||
}
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.node[key]
|
||||
|
||||
def check_filters(self, filters):
|
||||
return all((self.check(f) for f in filters))
|
||||
|
||||
def check(self, filter_string):
|
||||
if self.node["cluster"] is not None:
|
||||
return False
|
||||
m = FILTER_REG.match(filter_string)
|
||||
if m is None:
|
||||
raise ValueError("Invalid filter: %s" % filter_string)
|
||||
attribute, operator, value = m.groups()
|
||||
return self._check(attribute, value, operator)
|
||||
|
||||
def _check(self, attribute, value, operator):
|
||||
attribute = getattr(self, "_get_" + attribute)()
|
||||
checker = self.ATTRIBUTE_MAP[operator]
|
||||
m = INT_REG.match(value)
|
||||
if m:
|
||||
value = int(m.group(1)) * self.FACTOR_MAP[m.group(2)]
|
||||
return checker(attribute, value)
|
||||
|
||||
def _get_ram(self):
|
||||
return self.node["meta"]["memory"]["total"]
|
||||
|
||||
def _get_mac(self):
|
||||
return self.node["mac"]
|
||||
|
||||
def _get_storage(self):
|
||||
return sum((d["size"] for d in self.node["meta"]["disks"]))
|
||||
|
||||
def _get_cpus(self):
|
||||
return self.node["meta"]["cpu"]["total"]
|
||||
|
||||
|
||||
class FuelCluster(object):
|
||||
|
||||
def __init__(self, client, **config):
|
||||
"""Create Fuel cluster.
|
||||
|
||||
:param client: FuelClient instance.
|
||||
:param name: Name
|
||||
:param release: Release id. Integer.
|
||||
:param mode: One of multinode, ha_compact
|
||||
:param net_provider: One of nova_network, neutron
|
||||
:param net_segment_type: One of gre, vlan.
|
||||
:param dns_nameservers: List of strings.
|
||||
"""
|
||||
|
||||
self.client = client
|
||||
self.cluster = client.post("clusters", config)
|
||||
|
||||
def get_nodes(self):
|
||||
return self.client.get("nodes?cluster_id=%d" % self.cluster["id"])
|
||||
|
||||
def set_nodes(self, nodes, roles):
|
||||
if not nodes:
|
||||
return
|
||||
node_list = []
|
||||
for n in nodes:
|
||||
node_list.append({"id": n["id"],
|
||||
"pending_roles": roles,
|
||||
"pending_addition": True,
|
||||
"cluster_id": self.cluster["id"]})
|
||||
self.client.put("nodes", node_list)
|
||||
|
||||
def configure_network(self, config):
|
||||
netconfig = self.get_network()
|
||||
for network in netconfig["networks"]:
|
||||
if network["name"] in config:
|
||||
network.update(config[network["name"]])
|
||||
self.set_network(netconfig)
|
||||
|
||||
def deploy(self):
|
||||
self.client.put("clusters/%d/changes" % self.cluster["id"], {})
|
||||
for task in self.client.get_tasks(self.cluster["id"]):
|
||||
if task["name"] == "deploy":
|
||||
task_id = task["id"]
|
||||
break
|
||||
while 1:
|
||||
time.sleep(10)
|
||||
task = self.client.get_task(task_id)
|
||||
if task["progress"] == 100:
|
||||
return
|
||||
LOG.info("Deployment in progress. %d%% done." % task["progress"])
|
||||
|
||||
def get_network(self):
|
||||
args = {"cluster_id": self.cluster["id"],
|
||||
"net_provider": self.cluster["net_provider"]}
|
||||
url = ("clusters/%(cluster_id)d/network_configuration/"
|
||||
"%(net_provider)s" % args)
|
||||
return self.client.get(url)
|
||||
|
||||
def set_network(self, config):
|
||||
self.verify_network(config)
|
||||
args = {"cluster_id": self.cluster["id"],
|
||||
"net_provider": self.cluster["net_provider"]}
|
||||
url = ("clusters/%(cluster_id)d/network_configuration/"
|
||||
"%(net_provider)s" % args)
|
||||
self.client.put(url, config)
|
||||
|
||||
def verify_network(self, config):
|
||||
args = {"cluster_id": self.cluster["id"],
|
||||
"net_provider": self.cluster["net_provider"]}
|
||||
url = ("clusters/%(cluster_id)d/network_configuration/"
|
||||
"%(net_provider)s/verify" % args)
|
||||
task_id = self.client.put(url, config)["id"]
|
||||
while 1:
|
||||
time.sleep(5)
|
||||
task = self.client.get_task(task_id)
|
||||
if task["progress"] == 100:
|
||||
if task["message"]:
|
||||
raise FuelNetworkVerificationFailed(task["message"])
|
||||
else:
|
||||
return
|
||||
LOG.info("Network verification in progress."
|
||||
" %d%% done." % task["progress"])
|
||||
|
||||
def get_attributes(self):
|
||||
return self.client.get("clusters/%d/attributes" % self.cluster["id"])
|
||||
|
||||
def get_endpoint_ip(self):
|
||||
if self.cluster["mode"].startswith("ha_"):
|
||||
netdata = self.get_network()
|
||||
return netdata["public_vip"]
|
||||
|
||||
for node in self.get_nodes():
|
||||
if "controller" in node["roles"]:
|
||||
for net in node["network_data"]:
|
||||
if net["name"] == "public":
|
||||
return net["ip"].split("/")[0]
|
||||
|
||||
raise FuelException("Unable to get endpoint ip.")
|
||||
|
||||
|
||||
class FuelNodesCollection(object):
|
||||
nodes = []
|
||||
|
||||
def __init__(self, nodes):
|
||||
for node in nodes:
|
||||
self.nodes.append(FuelNode(node))
|
||||
|
||||
def pop(self, filters):
|
||||
for i, node in enumerate(self.nodes):
|
||||
if node.check_filters(filters):
|
||||
return self.nodes.pop(i)
|
||||
|
||||
|
||||
class FuelClient(object):
|
||||
|
||||
def __init__(self, base_url):
|
||||
self.base_url = base_url
|
||||
|
||||
def _request(self, method, url, data=None):
|
||||
if data:
|
||||
data = json.dumps(data)
|
||||
headers = {"content-type": "application/json"}
|
||||
reply = getattr(requests, method)(self.base_url + url, data=data,
|
||||
headers=headers)
|
||||
if reply.status_code >= 300 or reply.status_code < 200:
|
||||
raise FuelClientException(code=reply.status_code, body=reply.text)
|
||||
if reply.text and reply.headers["content-type"] == "application/json":
|
||||
return json.loads(reply.text)
|
||||
return reply
|
||||
|
||||
def get(self, url):
|
||||
return self._request("get", url)
|
||||
|
||||
def post(self, url, data):
|
||||
return self._request("post", url, data)
|
||||
|
||||
def put(self, url, data):
|
||||
return self._request("put", url, data)
|
||||
|
||||
def delete(self, url):
|
||||
return self._request("delete", url)
|
||||
|
||||
def get_releases(self):
|
||||
return self.get("releases")
|
||||
|
||||
def get_task(self, task_id):
|
||||
return self.get("tasks/%d" % task_id)
|
||||
|
||||
def get_tasks(self, cluster_id):
|
||||
return self.get("tasks?cluster_id=%d" % cluster_id)
|
||||
|
||||
def get_node(self, node_id):
|
||||
return self.get("nodes/%d" % node_id)
|
||||
|
||||
def get_nodes(self):
|
||||
return FuelNodesCollection(self.get("nodes"))
|
||||
|
||||
def delete_cluster(self, cluster_id):
|
||||
self.delete("clusters/%s" % cluster_id)
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"type": "FuelEngine",
|
||||
"deploy_name": "Rally HA 01",
|
||||
"release": "Havana on CentOS 6.4",
|
||||
"api_url": "http://10.20.0.2:8000/api/v1/",
|
||||
"mode": "ha_compact",
|
||||
"nodes": {
|
||||
"controller": {"amount": 3, "filters": ["storage>80G"]},
|
||||
"compute": {"amount": 1, "filters": ["storage>80G"]}
|
||||
},
|
||||
"net_provider": "nova_network",
|
||||
"dns_nameservers": ["172.18.208.44", "8.8.8.8"],
|
||||
"networks": {
|
||||
|
||||
"public": {
|
||||
"cidr": "10.3.5.0/24",
|
||||
"gateway": "10.3.5.1",
|
||||
"ip_ranges": [["10.3.5.5", "10.3.5.254"]],
|
||||
"vlan_start": 15
|
||||
},
|
||||
|
||||
"floating": {
|
||||
"cidr": "10.3.6.0/24",
|
||||
"ip_ranges": [["10.3.6.5", "10.3.6.254"]],
|
||||
"vlan_start": 15
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"type": "FuelEngine",
|
||||
"deploy_name": "Rally multinode 01",
|
||||
"release": "Havana on CentOS 6.4",
|
||||
"api_url": "http://10.20.0.2:8000/api/v1/",
|
||||
"mode": "multinode",
|
||||
"nodes": {
|
||||
"controller": {"amount": 1, "filters": ["storage>80G"]},
|
||||
"compute": {"amount": 1, "filters": ["storage>80G"]}
|
||||
},
|
||||
"net_provider": "nova_network",
|
||||
"dns_nameservers": ["172.18.208.44", "8.8.8.8"],
|
||||
"networks": {
|
||||
|
||||
"public": {
|
||||
"cidr": "10.3.3.0/24",
|
||||
"gateway": "10.3.3.1",
|
||||
"ip_ranges": [["10.3.3.5", "10.3.3.254"]],
|
||||
"vlan_start": 14
|
||||
},
|
||||
|
||||
"floating": {
|
||||
"cidr": "10.3.4.0/24",
|
||||
"ip_ranges": [["10.3.4.5", "10.3.4.254"]],
|
||||
"vlan_start": 14
|
||||
}
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from rally import consts
|
||||
from rally.deployment.engines import fuel
|
||||
from rally import exceptions
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
SAMPLE_CONFIG = {
|
||||
"type": "FuelEngine",
|
||||
"deploy_name": "TestDeploy01",
|
||||
"net_provider": "nova_network",
|
||||
"release": "Havana on Ubuntu 12.04",
|
||||
"api_url": "http://example.net:8000/api/v1/",
|
||||
"mode": "multinode",
|
||||
"networks": {"public": {"cidr": "10.1.1.0/24"}},
|
||||
"nodes": {
|
||||
"controller": {"amount": 1, "filters": ["cpus==2"]},
|
||||
"cinder+compute": {"amount": 4, "filters": ["cpus==8",
|
||||
"storage>=2T"]},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class FuelEngineTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FuelEngineTestCase, self).setUp()
|
||||
self.deployment = fakes.FakeDeployment({"config": SAMPLE_CONFIG})
|
||||
|
||||
def test_construct(self):
|
||||
fuel.FuelEngine(self.deployment)
|
||||
|
||||
def test_validate_no_computes(self):
|
||||
config = SAMPLE_CONFIG.copy()
|
||||
config["nodes"].pop("cinder+compute")
|
||||
deployment = {"config": config}
|
||||
engine = fuel.FuelEngine(deployment)
|
||||
self.assertRaises(exceptions.ValidationError,
|
||||
engine.validate)
|
||||
|
||||
def test__get_nodes(self):
|
||||
engine = fuel.FuelEngine(self.deployment)
|
||||
engine.nodes = mock.MagicMock()
|
||||
engine.nodes.pop.side_effect = [1, 2, 3, 4]
|
||||
nodes = engine._get_nodes("cinder+compute")
|
||||
self.assertEqual([1, 2, 3, 4], nodes)
|
||||
expected_calls = [mock.call(["cpus==8", "storage>=2T"])] * 4
|
||||
self.assertEqual(expected_calls, engine.nodes.pop.mock_calls)
|
||||
|
||||
def test__get_nodes_no_nodes(self):
|
||||
engine = fuel.FuelEngine(self.deployment)
|
||||
engine.nodes = mock.MagicMock()
|
||||
engine.nodes.pop.return_value = None
|
||||
self.assertRaises(exceptions.NoNodesFound,
|
||||
engine._get_nodes, "controller")
|
||||
|
||||
def test__get_nodes_empty(self):
|
||||
engine = fuel.FuelEngine(self.deployment)
|
||||
self.assertEqual([], engine._get_nodes("nonexistent"))
|
||||
|
||||
def test__get_release_id(self):
|
||||
engine = fuel.FuelEngine(self.deployment)
|
||||
engine.client = mock.Mock()
|
||||
fake_releases = [{"name": "fake", "id": 1},
|
||||
{"name": "Havana on Ubuntu 12.04", "id": 42}]
|
||||
engine.client.get_releases = mock.Mock(return_value=fake_releases)
|
||||
self.assertEqual(42, engine._get_release_id())
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.FuelClient")
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.FuelCluster")
|
||||
def test_deploy(self, mock_fuel_cluster, mock_fuel_client):
|
||||
attributes = {"editable": {"access": {"user": {"value": "user"},
|
||||
"password": {"value": "pw"},
|
||||
"tenant": {"value": "tn"}}}}
|
||||
client = mock.Mock()
|
||||
cluster = mock.Mock(
|
||||
cluster={"id": 42},
|
||||
**{
|
||||
"get_endpoint_ip.return_value": "2.3.4.5",
|
||||
"get_attributes.return_value": attributes
|
||||
}
|
||||
)
|
||||
mock_fuel_client.return_value = client
|
||||
mock_fuel_cluster.return_value = cluster
|
||||
self.deployment.add_resource = mock.Mock()
|
||||
|
||||
engine = fuel.FuelEngine(self.deployment)
|
||||
|
||||
engine._get_nodes = mock.Mock(side_effect=[1, 2, 3, 4])
|
||||
engine._get_release_id = mock.Mock()
|
||||
|
||||
credential = engine.deploy()
|
||||
self.assertEqual(["admin"], list(credential))
|
||||
credential = credential["admin"]
|
||||
|
||||
self.assertEqual("user", credential.username)
|
||||
self.assertEqual("pw", credential.password)
|
||||
self.assertEqual("tn", credential.tenant_name)
|
||||
self.assertEqual("http://2.3.4.5:5000/v2.0/", credential.auth_url)
|
||||
self.assertEqual(consts.EndpointPermission.ADMIN,
|
||||
credential.permission)
|
||||
|
||||
expected_cluster_calls = [
|
||||
mock.call.set_nodes(1, ["controller"]),
|
||||
mock.call.set_nodes(2, ["compute"]),
|
||||
mock.call.set_nodes(3, ["cinder"]),
|
||||
mock.call.set_nodes(4, ["compute", "cinder"]),
|
||||
mock.call.configure_network({"public": {"cidr": "10.1.1.0/24"}}),
|
||||
mock.call.deploy(),
|
||||
mock.call.get_endpoint_ip(),
|
||||
mock.call.get_attributes()
|
||||
]
|
||||
self.assertEqual(expected_cluster_calls, cluster.mock_calls)
|
||||
self.assertEqual([mock.call.get_nodes()], client.mock_calls)
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.FuelClient")
|
||||
@mock.patch("rally.deployment.engines.fuel.objects.Deployment")
|
||||
def test_cleanup(self, mock_deployment, mock_fuel_client):
|
||||
fake_resources = [{"id": 41, "info": {"id": 42}}]
|
||||
self.deployment.get_resources = mock.Mock(return_value=fake_resources)
|
||||
|
||||
engine = fuel.FuelEngine(self.deployment)
|
||||
engine.client = mock.Mock()
|
||||
engine.cleanup()
|
||||
|
||||
engine.client.delete_cluster.assert_called_once_with(42)
|
||||
mock_deployment.delete_resource.assert_called_once_with(41)
|
@ -1,340 +0,0 @@
|
||||
# Copyright 2013: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
|
||||
from rally.deployment.fuel import fuelclient
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
class FuelNodeTestCase(test.TestCase):
|
||||
|
||||
def test_check(self):
|
||||
|
||||
node = {
|
||||
"cluster": None,
|
||||
"mac": "00:01:02:0a:0b:0c",
|
||||
"meta": {
|
||||
"memory": {"total": 42},
|
||||
"cpu": {"total": 2},
|
||||
"disks": [{"size": 22}, {"size": 33}] # total 55
|
||||
},
|
||||
}
|
||||
|
||||
n = fuelclient.FuelNode(node)
|
||||
|
||||
self.assertFalse(n.check("ram==41"))
|
||||
self.assertFalse(n.check("ram!=42"))
|
||||
self.assertFalse(n.check("ram<=41"))
|
||||
self.assertFalse(n.check("ram>=43"))
|
||||
self.assertFalse(n.check("ram>43"))
|
||||
self.assertFalse(n.check("ram<41"))
|
||||
self.assertFalse(n.check("cpus>3"))
|
||||
|
||||
self.assertTrue(n.check("ram==42"))
|
||||
self.assertTrue(n.check("ram!=41"))
|
||||
self.assertTrue(n.check("ram<=43"))
|
||||
self.assertTrue(n.check("ram<=42"))
|
||||
self.assertTrue(n.check("ram>=41"))
|
||||
self.assertTrue(n.check("ram>=42"))
|
||||
self.assertTrue(n.check("ram<43"))
|
||||
self.assertTrue(n.check("ram>41"))
|
||||
self.assertTrue(n.check("cpus==2"))
|
||||
|
||||
self.assertTrue(n.check("mac==00:01:02:0a:0b:0c"))
|
||||
self.assertTrue(n.check("mac!=00:01:02:0a:0b:0e"))
|
||||
self.assertTrue(n.check("storage==55"))
|
||||
self.assertTrue(n.check("storage<=1G"))
|
||||
self.assertTrue(n.check("storage<1M"))
|
||||
|
||||
|
||||
class FuelNodesCollectionTestCase(test.TestCase):
|
||||
|
||||
def test_pop(self):
|
||||
node = {
|
||||
"cluster": None,
|
||||
"mac": "00:01:02:0a:0b:0c",
|
||||
"meta": {
|
||||
"memory": {"total": 42},
|
||||
"cpu": {"total": 2},
|
||||
"disks": [{"size": 22}, {"size": 33}] # total 55
|
||||
},
|
||||
}
|
||||
|
||||
nodes = [copy.deepcopy(node) for i in range(4)]
|
||||
nodes[0]["meta"]["cpu"]["total"] = 1
|
||||
nodes[1]["meta"]["cpu"]["total"] = 2
|
||||
nodes[2]["meta"]["memory"]["total"] = 16
|
||||
nodes[3]["cluster"] = 42 # node with cluster is occupied
|
||||
|
||||
nodes = fuelclient.FuelNodesCollection(nodes)
|
||||
|
||||
node_1cpu = nodes.pop(["cpus==1"])
|
||||
self.assertEqual(node_1cpu._get_cpus(), 1)
|
||||
self.assertEqual(len(nodes.nodes), 3)
|
||||
|
||||
node_2cpu = nodes.pop(["cpus==2"])
|
||||
self.assertEqual(node_2cpu._get_cpus(), 2)
|
||||
self.assertEqual(len(nodes.nodes), 2)
|
||||
|
||||
node_16ram_2cpu = nodes.pop(["ram>=16", "cpus==2"])
|
||||
self.assertEqual(node_16ram_2cpu._get_ram(), 16)
|
||||
self.assertEqual(node_16ram_2cpu._get_cpus(), 2)
|
||||
self.assertEqual(len(nodes.nodes), 1)
|
||||
node_none = nodes.pop(["storage>4T"])
|
||||
self.assertIsNone(node_none)
|
||||
|
||||
|
||||
class FuelClusterTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FuelClusterTestCase, self).setUp()
|
||||
self.client = mock.Mock()
|
||||
self.config = {"name": "Cluster"}
|
||||
self.cluster = fuelclient.FuelCluster(self.client, **self.config)
|
||||
|
||||
def test_init(self):
|
||||
self.client.post.assert_called_once_with("clusters", self.config)
|
||||
|
||||
def test_get_nodes(self):
|
||||
self.cluster.cluster = {"id": 42}
|
||||
self.cluster.get_nodes()
|
||||
self.client.get.assert_called_once_with("nodes?cluster_id=42")
|
||||
|
||||
def test_set_nodes_empty(self):
|
||||
self.assertIsNone(self.cluster.set_nodes([], []))
|
||||
|
||||
def test_set_nodes(self):
|
||||
nodes = [{"id": 42}, {"id": 43}]
|
||||
self.cluster.cluster = {"id": 1}
|
||||
self.cluster.set_nodes(nodes, ["role1", "role2"])
|
||||
|
||||
node42_args = {"cluster_id": 1,
|
||||
"pending_roles": ["role1", "role2"],
|
||||
"pending_addition": True,
|
||||
"id": 42}
|
||||
node43_args = {"cluster_id": 1,
|
||||
"pending_roles": ["role1", "role2"],
|
||||
"pending_addition": True,
|
||||
"id": 43}
|
||||
expected = [
|
||||
mock.call.post("clusters", {"name": "Cluster"}),
|
||||
mock.call.put("nodes", [node42_args, node43_args])
|
||||
]
|
||||
self.assertEqual(expected, self.client.mock_calls)
|
||||
|
||||
def test_configure_network(self):
|
||||
current_network = {"networks": [{"name": "public",
|
||||
"key": "old_val",
|
||||
"key2": "val2"}]}
|
||||
|
||||
netconfig = {"public": {"key": "new_val"}}
|
||||
self.cluster.get_network = mock.Mock(return_value=current_network)
|
||||
self.cluster.set_network = mock.Mock()
|
||||
|
||||
self.cluster.configure_network(netconfig)
|
||||
|
||||
self.cluster.set_network.assert_called_once_with(
|
||||
{"networks": [{"name": "public",
|
||||
"key": "new_val",
|
||||
"key2": "val2"}]})
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.time.sleep")
|
||||
def test_deploy(self, mock_sleep):
|
||||
call1 = {"progress": 50}
|
||||
call2 = {"progress": 100}
|
||||
self.client.get_task.side_effect = [call1, call2]
|
||||
|
||||
tasks = [{"name": "deploy", "id": 41}]
|
||||
self.client.get_tasks.return_value = tasks
|
||||
|
||||
self.cluster.cluster = {"id": 42}
|
||||
self.cluster.deploy()
|
||||
|
||||
expected = [
|
||||
mock.call.post("clusters", {"name": "Cluster"}),
|
||||
mock.call.put("clusters/42/changes", {}),
|
||||
mock.call.get_tasks(42),
|
||||
mock.call.get_task(41),
|
||||
mock.call.get_task(41)
|
||||
]
|
||||
self.assertEqual(expected, self.client.mock_calls)
|
||||
|
||||
def test_get_network(self):
|
||||
self.cluster.cluster = {"id": 42, "net_provider": "nova_network"}
|
||||
self.cluster.get_network()
|
||||
self.client.get.assert_called_once_with(
|
||||
"clusters/42/network_configuration/nova_network")
|
||||
|
||||
def test_set_network(self):
|
||||
self.cluster.cluster = {"id": 42, "net_provider": "nova_network"}
|
||||
self.cluster.verify_network = mock.Mock()
|
||||
self.cluster.set_network({"key": "val"})
|
||||
|
||||
self.client.put.assert_called_once_with(
|
||||
"clusters/42/network_configuration/nova_network", {"key": "val"})
|
||||
self.cluster.verify_network.assert_called_once_with({"key": "val"})
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.time.sleep")
|
||||
def test_verify_network(self, mock_sleep):
|
||||
call1 = {"progress": 50}
|
||||
call2 = {"progress": 100, "message": ""}
|
||||
|
||||
self.client.put.return_value = {"id": 42}
|
||||
self.client.get_task.side_effect = [call1, call2]
|
||||
self.cluster.cluster = {"id": 43, "net_provider": "nova_network"}
|
||||
|
||||
self.cluster.verify_network({"key": "val"})
|
||||
|
||||
self.client.put.assert_called_once_with(
|
||||
"clusters/43/network_configuration/nova_network/verify",
|
||||
{"key": "val"})
|
||||
self.assertEqual([mock.call(42), mock.call(42)],
|
||||
self.client.get_task.mock_calls)
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.time.sleep")
|
||||
def test_verify_network_fail(self, mock_sleep):
|
||||
self.client.put.return_value = {"id": 42}
|
||||
self.client.get_task.return_value = {"progress": 100,
|
||||
"message": "error"}
|
||||
self.cluster.cluster = {"id": 43, "net_provider": "nova_network"}
|
||||
self.assertRaises(fuelclient.FuelNetworkVerificationFailed,
|
||||
self.cluster.verify_network, {"key": "val"})
|
||||
|
||||
def test_get_attributes(self):
|
||||
self.cluster.cluster = {"id": 52}
|
||||
self.cluster.get_attributes()
|
||||
self.client.get.assert_called_once_with("clusters/52/attributes")
|
||||
|
||||
def test_get_endpoint_ip_multinode(self):
|
||||
self.cluster.cluster = {"mode": "multinode"}
|
||||
node1 = {"roles": ["compute", "cinder"]}
|
||||
node2 = {"roles": ["controller"],
|
||||
"network_data": [{"name": "private"},
|
||||
{"name": "public", "ip": "42.42.42.42/24"}]}
|
||||
fake_nodes = [node1, node2]
|
||||
self.cluster.get_nodes = mock.Mock(return_value=fake_nodes)
|
||||
ip = self.cluster.get_endpoint_ip()
|
||||
self.assertEqual("42.42.42.42", ip)
|
||||
|
||||
def test_get_endpoint_ip_ha(self):
|
||||
ip = "1.2.3.4"
|
||||
self.cluster.cluster = {"id": 42, "mode": "ha_compact"}
|
||||
self.cluster.get_network = mock.Mock(return_value={"public_vip": ip})
|
||||
self.assertEqual(ip, self.cluster.get_endpoint_ip())
|
||||
|
||||
|
||||
class FuelClientTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FuelClientTestCase, self).setUp()
|
||||
self.client = fuelclient.FuelClient("http://10.20.0.2:8000/api/v1/")
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.requests")
|
||||
def test__request_non_json(self, mock_requests):
|
||||
reply = mock.Mock()
|
||||
reply.status_code = 200
|
||||
reply.headers = {"content-type": "application/x-httpd-php"}
|
||||
reply.text = "{\"reply\": \"ok\"}"
|
||||
mock_requests.method.return_value = reply
|
||||
|
||||
retval = self.client._request("method", "url", data={"key": "value"})
|
||||
|
||||
self.assertEqual(retval, reply)
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.requests")
|
||||
def test__request_non_2xx(self, mock_requests):
|
||||
reply = mock.Mock()
|
||||
reply.status_code = 300
|
||||
reply.headers = {"content-type": "application/json"}
|
||||
reply.text = "{\"reply\": \"ok\"}"
|
||||
mock_requests.method.return_value = reply
|
||||
self.assertRaises(fuelclient.FuelClientException,
|
||||
self.client._request, "method", "url",
|
||||
data={"key": "value"})
|
||||
|
||||
@mock.patch("rally.deployment.fuel.fuelclient.requests")
|
||||
def test__request(self, mock_requests):
|
||||
reply = mock.Mock()
|
||||
reply.status_code = 202
|
||||
reply.headers = {"content-type": "application/json"}
|
||||
reply.text = "{\"reply\": \"ok\"}"
|
||||
mock_requests.method.return_value = reply
|
||||
|
||||
retval = self.client._request("method", "url", data={"key": "value"})
|
||||
mock_requests.method.assert_called_once_with(
|
||||
"http://10.20.0.2:8000/api/v1/url",
|
||||
headers={"content-type": "application/json"},
|
||||
data="{\"key\": \"value\"}")
|
||||
self.assertEqual(retval, {"reply": "ok"})
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "_request")
|
||||
def test_get(self, mock_fuel_client__request):
|
||||
self.client.get("url")
|
||||
mock_fuel_client__request.assert_called_once_with("get", "url")
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "_request")
|
||||
def test_delete(self, mock_fuel_client__request):
|
||||
self.client.delete("url")
|
||||
mock_fuel_client__request.assert_called_once_with("delete", "url")
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "_request")
|
||||
def test_post(self, mock_fuel_client__request):
|
||||
self.client.post("url", {"key": "val"})
|
||||
mock_fuel_client__request.assert_called_once_with(
|
||||
"post", "url", {"key": "val"})
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "_request")
|
||||
def test_put(self, mock_fuel_client__request):
|
||||
self.client.put("url", {"key": "val"})
|
||||
mock_fuel_client__request.assert_called_once_with(
|
||||
"put", "url", {"key": "val"})
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "get")
|
||||
def test_get_releases(self, mock_fuel_client_get):
|
||||
self.client.get_releases()
|
||||
mock_fuel_client_get.assert_called_once_with("releases")
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "get")
|
||||
def test_get_task(self, mock_fuel_client_get):
|
||||
self.client.get_task(42)
|
||||
mock_fuel_client_get.assert_called_once_with("tasks/42")
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "get")
|
||||
def test_get_tasks(self, mock_fuel_client_get):
|
||||
self.client.get_tasks(42)
|
||||
mock_fuel_client_get.assert_called_once_with("tasks?cluster_id=42")
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "get")
|
||||
def test_get_node(self, mock_fuel_client_get):
|
||||
self.client.get_node(42)
|
||||
mock_fuel_client_get.assert_called_once_with("nodes/42")
|
||||
|
||||
@mock.patch.object(fuelclient, "FuelNodesCollection")
|
||||
@mock.patch.object(fuelclient.FuelClient, "get")
|
||||
def test_get_nodes(self, mock_fuel_client_get, mock_fuel_nodes_collection):
|
||||
mock_fuel_client_get.return_value = "fake_nodes"
|
||||
mock_fuel_nodes_collection.return_value = "fake_collection"
|
||||
retval = self.client.get_nodes()
|
||||
self.assertEqual("fake_collection", retval)
|
||||
mock_fuel_nodes_collection.assert_called_once_with("fake_nodes")
|
||||
mock_fuel_client_get.assert_called_once_with("nodes")
|
||||
|
||||
@mock.patch.object(fuelclient.FuelClient, "delete")
|
||||
def test_delete_cluster(self, mock_fuel_client_delete):
|
||||
self.client.delete_cluster(42)
|
||||
mock_fuel_client_delete.assert_called_once_with("clusters/42")
|
Loading…
Reference in New Issue
Block a user