Merge "[Ci] Get changes in resources after task run"
This commit is contained in:
commit
d57663d2ce
292
tests/ci/osresources.py
Executable file
292
tests/ci/osresources.py
Executable file
@ -0,0 +1,292 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
"""List and compare most used OpenStack cloud resources."""
|
||||||
|
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from rally.common.plugin import discover
|
||||||
|
from rally import consts
|
||||||
|
from rally import osclients
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceManager(object):
|
||||||
|
|
||||||
|
REQUIRED_SERVICE = None
|
||||||
|
REPR_KEYS = ("id", "name", "tenant_id", "zone", "zoneName", "pool")
|
||||||
|
|
||||||
|
def __init__(self, clients):
|
||||||
|
self.clients = clients
|
||||||
|
|
||||||
|
def is_available(self):
|
||||||
|
if self.REQUIRED_SERVICE:
|
||||||
|
return self.REQUIRED_SERVICE in self.clients.services().values()
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def client(self):
|
||||||
|
return getattr(self.clients, self.__class__.__name__.lower())()
|
||||||
|
|
||||||
|
def get_resources(self):
|
||||||
|
all_resources = []
|
||||||
|
cls = self.__class__.__name__.lower()
|
||||||
|
for prop in dir(self):
|
||||||
|
if not prop.startswith("list_"):
|
||||||
|
continue
|
||||||
|
f = getattr(self, prop)
|
||||||
|
resources = f() or []
|
||||||
|
resource_name = prop[5:][:-1]
|
||||||
|
|
||||||
|
for res in resources:
|
||||||
|
res_repr = []
|
||||||
|
for key in self.REPR_KEYS + (resource_name,):
|
||||||
|
if isinstance(res, dict):
|
||||||
|
value = res.get(key)
|
||||||
|
else:
|
||||||
|
value = getattr(res, key, None)
|
||||||
|
if value:
|
||||||
|
res_repr.append("%s:%s" % (key, value))
|
||||||
|
if not res_repr:
|
||||||
|
raise ValueError("Failed to represent resource %r" % res)
|
||||||
|
|
||||||
|
all_resources.append(
|
||||||
|
"%s %s %s" % (cls, resource_name, " ".join(res_repr)))
|
||||||
|
return all_resources
|
||||||
|
|
||||||
|
|
||||||
|
class Keystone(ResourceManager):
|
||||||
|
|
||||||
|
def list_users(self):
|
||||||
|
return self.client.users.list()
|
||||||
|
|
||||||
|
def list_tenants(self):
|
||||||
|
return self.client.tenants.list()
|
||||||
|
|
||||||
|
def list_roles(self):
|
||||||
|
return self.client.roles.list()
|
||||||
|
|
||||||
|
|
||||||
|
class Nova(ResourceManager):
|
||||||
|
|
||||||
|
def list_flavors(self):
|
||||||
|
return self.client.flavors.list()
|
||||||
|
|
||||||
|
def list_floating_ip_pools(self):
|
||||||
|
return self.client.floating_ip_pools.list()
|
||||||
|
|
||||||
|
def list_floating_ips(self):
|
||||||
|
return self.client.floating_ips.list()
|
||||||
|
|
||||||
|
def list_images(self):
|
||||||
|
return self.client.images.list()
|
||||||
|
|
||||||
|
def list_keypairs(self):
|
||||||
|
return self.client.keypairs.list()
|
||||||
|
|
||||||
|
def list_networks(self):
|
||||||
|
return self.client.networks.list()
|
||||||
|
|
||||||
|
def list_security_groups(self):
|
||||||
|
return self.client.security_groups.list(
|
||||||
|
search_opts={"all_tenants": True})
|
||||||
|
|
||||||
|
def list_servers(self):
|
||||||
|
return self.client.servers.list(
|
||||||
|
search_opts={"all_tenants": True})
|
||||||
|
|
||||||
|
def list_services(self):
|
||||||
|
return self.client.services.list()
|
||||||
|
|
||||||
|
def list_availability_zones(self):
|
||||||
|
return self.client.availability_zones.list()
|
||||||
|
|
||||||
|
|
||||||
|
class Neutron(ResourceManager):
|
||||||
|
|
||||||
|
REQUIRED_SERVICE = consts.Service.NEUTRON
|
||||||
|
|
||||||
|
def has_extension(self, name):
|
||||||
|
extensions = self.client.list_extensions().get("extensions", [])
|
||||||
|
return any(ext.get("alias") == name for ext in extensions)
|
||||||
|
|
||||||
|
def list_networks(self):
|
||||||
|
return self.client.list_networks()["networks"]
|
||||||
|
|
||||||
|
def list_subnets(self):
|
||||||
|
return self.client.list_subnets()["subnets"]
|
||||||
|
|
||||||
|
def list_routers(self):
|
||||||
|
return self.client.list_routers()["routers"]
|
||||||
|
|
||||||
|
def list_ports(self):
|
||||||
|
return self.client.list_ports()["ports"]
|
||||||
|
|
||||||
|
def list_floatingips(self):
|
||||||
|
return self.client.list_floatingips()["floatingips"]
|
||||||
|
|
||||||
|
def list_security_groups(self):
|
||||||
|
return self.client.list_security_groups()["security_groups"]
|
||||||
|
|
||||||
|
def list_health_monitors(self):
|
||||||
|
if self.has_extension("lbaas"):
|
||||||
|
return self.client.list_health_monitors()["health_monitors"]
|
||||||
|
|
||||||
|
def list_pools(self):
|
||||||
|
if self.has_extension("lbaas"):
|
||||||
|
return self.client.list_pools()["pools"]
|
||||||
|
|
||||||
|
def list_vips(self):
|
||||||
|
if self.has_extension("lbaas"):
|
||||||
|
return self.client.list_vips()["vips"]
|
||||||
|
|
||||||
|
|
||||||
|
class Glance(ResourceManager):
|
||||||
|
|
||||||
|
def list_images(self):
|
||||||
|
return self.client.images.list()
|
||||||
|
|
||||||
|
|
||||||
|
class Heat(ResourceManager):
|
||||||
|
|
||||||
|
def list_resource_types(self):
|
||||||
|
return self.client.resource_types.list()
|
||||||
|
|
||||||
|
def list_stacks(self):
|
||||||
|
return self.client.stacks.list()
|
||||||
|
|
||||||
|
|
||||||
|
class Cinder(ResourceManager):
|
||||||
|
|
||||||
|
def list_availability_zones(self):
|
||||||
|
return self.client.availability_zones.list()
|
||||||
|
|
||||||
|
def list_backups(self):
|
||||||
|
return self.client.backups.list()
|
||||||
|
|
||||||
|
def list_volume_snapshots(self):
|
||||||
|
return self.client.volume_snapshots.list()
|
||||||
|
|
||||||
|
def list_volume_types(self):
|
||||||
|
return self.client.volume_types.list()
|
||||||
|
|
||||||
|
def list_volumes(self):
|
||||||
|
return self.client.volumes.list(
|
||||||
|
search_opts={"all_tenants": True})
|
||||||
|
|
||||||
|
|
||||||
|
class CloudResources(object):
|
||||||
|
"""List and compare cloud resources.
|
||||||
|
|
||||||
|
resources = CloudResources(auth_url=..., ...)
|
||||||
|
saved_list = resources.list()
|
||||||
|
|
||||||
|
# Do something with the cloud ...
|
||||||
|
|
||||||
|
changes = resources.compare(saved_list)
|
||||||
|
has_changed = any(changes)
|
||||||
|
removed, added = changes
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
endpoint = osclients.objects.Endpoint(**kwargs)
|
||||||
|
self.clients = osclients.Clients(endpoint)
|
||||||
|
|
||||||
|
def _deduplicate(self, lst):
|
||||||
|
"""Change list duplicates to make all items unique.
|
||||||
|
|
||||||
|
>>> resources._deduplicate(["a", "b", "c", "b", "b"])
|
||||||
|
>>> ['a', 'b', 'c', 'b (duplicate 1)', 'b (duplicate 2)'
|
||||||
|
"""
|
||||||
|
deduplicated_list = []
|
||||||
|
for value in lst:
|
||||||
|
if value in deduplicated_list:
|
||||||
|
ctr = 0
|
||||||
|
try_value = value
|
||||||
|
while try_value in deduplicated_list:
|
||||||
|
ctr += 1
|
||||||
|
try_value = "%s (duplicate %i)" % (value, ctr)
|
||||||
|
value = try_value
|
||||||
|
deduplicated_list.append(value)
|
||||||
|
return deduplicated_list
|
||||||
|
|
||||||
|
def list(self):
|
||||||
|
managers_classes = discover.itersubclasses(ResourceManager)
|
||||||
|
resources = []
|
||||||
|
for cls in managers_classes:
|
||||||
|
manager = cls(self.clients)
|
||||||
|
if manager.is_available():
|
||||||
|
resources.extend(manager.get_resources())
|
||||||
|
return sorted(self._deduplicate(resources))
|
||||||
|
|
||||||
|
def compare(self, with_list):
|
||||||
|
saved_resources = set(with_list)
|
||||||
|
current_resources = set(self.list())
|
||||||
|
removed = saved_resources - current_resources
|
||||||
|
added = current_resources - saved_resources
|
||||||
|
|
||||||
|
return sorted(list(removed)), sorted(list(added))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=("Save list of OpenStack cloud resources or compare "
|
||||||
|
"with previously saved list."))
|
||||||
|
parser.add_argument("--credentials",
|
||||||
|
type=argparse.FileType("r"),
|
||||||
|
metavar="<path/to/credentials.json>",
|
||||||
|
help="cloud credentials in JSON format")
|
||||||
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
group.add_argument("--dump-list",
|
||||||
|
type=argparse.FileType("w"),
|
||||||
|
metavar="<path/to/output/list.json>",
|
||||||
|
help="dump resources to given file in JSON format")
|
||||||
|
group.add_argument("--compare-with-list",
|
||||||
|
type=argparse.FileType("r"),
|
||||||
|
metavar="<path/to/existent/list.json>",
|
||||||
|
help=("compare current resources with a list from "
|
||||||
|
"given JSON file"))
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.credentials:
|
||||||
|
config = json.load(args.credentials)
|
||||||
|
else:
|
||||||
|
config = json.loads(subprocess.check_output(["rally", "deployment",
|
||||||
|
"config"]))
|
||||||
|
config.update(config.pop("admin"))
|
||||||
|
del config["type"]
|
||||||
|
|
||||||
|
resources = CloudResources(**config)
|
||||||
|
|
||||||
|
if args.dump_list:
|
||||||
|
resources_list = resources.list()
|
||||||
|
json.dump(resources_list, args.dump_list, indent=2)
|
||||||
|
elif args.compare_with_list:
|
||||||
|
given_list = json.load(args.compare_with_list)
|
||||||
|
changes = resources.compare(with_list=given_list)
|
||||||
|
removed, added = changes
|
||||||
|
sys.stdout.write(
|
||||||
|
json.dumps({"removed": removed, "added": added}, indent=2))
|
||||||
|
if any(changes):
|
||||||
|
return 0 # `1' will fail gate job
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
@ -60,6 +60,9 @@ rally show networks
|
|||||||
rally show secgroups
|
rally show secgroups
|
||||||
rally show keypairs
|
rally show keypairs
|
||||||
|
|
||||||
|
python $BASE/new/rally/tests/ci/osresources.py\
|
||||||
|
--dump-list resources_at_start.txt
|
||||||
|
|
||||||
rally -v --rally-debug task start --task $TASK $TASK_ARGS
|
rally -v --rally-debug task start --task $TASK $TASK_ARGS
|
||||||
|
|
||||||
mkdir -p rally-plot/extra
|
mkdir -p rally-plot/extra
|
||||||
@ -75,3 +78,8 @@ gzip -9 rally-plot/detailed_with_iterations.txt
|
|||||||
rally task report --out rally-plot/results.html
|
rally task report --out rally-plot/results.html
|
||||||
gzip -9 rally-plot/results.html
|
gzip -9 rally-plot/results.html
|
||||||
rally task sla_check | tee rally-plot/sla.txt
|
rally task sla_check | tee rally-plot/sla.txt
|
||||||
|
|
||||||
|
python $BASE/new/rally/tests/ci/osresources.py\
|
||||||
|
--compare-with-list resources_at_start.txt\
|
||||||
|
| gzip > rally-plot/resources_diff.txt.gz
|
||||||
|
cp resources_at_start.txt rally-plot/
|
||||||
|
Loading…
Reference in New Issue
Block a user