diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..d243ecf --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,42 @@ +// Copyright (c) 2020, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +pipeline { + options { + disableConcurrentBuilds() + } + + agent { + docker { + image "${RUNNER_IMAGE}" + } + } + + stages { + stage('Build') { + steps { + sh """ + # create virtual environment + sudo pip-3.6 install virtualenv + virtualenv ~/.venvs/chaostk + + # enter the python virtual environment + source ~/.venvs/chaostk/bin/activate + + # install oci sdk + pip-3.6 install oci + + # install dependencies + pip-3.6 install -r requirements.txt -r requirements-dev.txt + chaos --version + + # setup dev environment + python3.6 setup.py develop + + # run the CI script + bash ./ci.bash + """ + } + } + } +} \ No newline at end of file diff --git a/chaosoci/core/compute/actions.py b/chaosoci/core/compute/actions.py index 611e144..075b194 100644 --- a/chaosoci/core/compute/actions.py +++ b/chaosoci/core/compute/actions.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- +from collections import defaultdict from random import choice from typing import Any, Dict, List -from chaoslib.exceptions import ActivityFailed +import oci +from chaoslib.exceptions import ActivityFailed, FailedActivity from chaoslib.types import Configuration, Secrets +from oci.retry import DEFAULT_RETRY_STRATEGY from chaosoci import oci_client from chaosoci.types import OCIResponse @@ -11,14 +14,21 @@ from logzero import logger from oci.config import from_file -from oci.core import ComputeClient +from oci.core import ComputeClient, ComputeManagementClient from .common import (filter_instances, - get_instances) + get_instances, get_instance_pools, filter_instance_pools) -__all__ = ["stop_instance", "stop_random_instance"] +__all__ = ["stop_instance", "stop_random_instance", "stop_instances_in_compartment", + "start_instance_pool", "start_all_instance_pools_in_compartment", + "stop_instance_pool", "stop_all_instance_pools_in_compartment", + "terminate_instance_pool", "terminate_all_instance_pools_in_compartment", + "reset_instance_pool", "reset_all_instance_pools_in_compartment", + "softreset_instance_pool", "softreset_all_instance_pools_in_compartment"] +# Compute Client Actions + def stop_instance(instance_id: str, force: bool = False, configuration: Configuration = None, secrets: Secrets = None) -> OCIResponse: @@ -68,3 +78,341 @@ def stop_random_instance(filters: List[Dict[str, Any]], ret = s_client.instance_action(instance_id=instance_id, action=action) return ret.data + + +def stop_instances_in_compartment(filters: List[Dict[str, Any]], + instances_ids: List[str] = None, + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> OCIResponse: + """Stop the given OCI Compute instances, If only an Compartment is specified, all instances in + that Compartment will be stopped. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.Instance.html#oci.core.models.Instance + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ComputeClient, configuration, secrets, + skip_deserialization=True) + if not instances_ids: + logger.warning('Based on configuration provided I am going to ' + 'stop all instances in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + instances = get_instances(client, compartment_id) + + filters = filters or None + if filters is not None: + instances_ids = filter_instances(instances, filters=filters) + + if not instances_ids: + raise FailedActivity( + 'No instances found matching filters: %s' % str(filters)) + + logger.debug('Instances in Compartment %s selected: %s}.' % ( + compartment_id, str(instances_ids))) + + stop_instances_ret = [] + + for instance_id in instances_ids: + logger.debug("Picked Compute Instance '{}' from Compartment '{}' to be stopped", instance_id, compartment_id) + + stop_instances_ret.append(stop_instance(instance_id, False)) + + return stop_instances_ret + + +# Compute Client Management Actions + +def stop_instance_pool(instance_pool_id: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Stop the given OCI Compute instance pool.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + stop_instance_pool_response = client.stop_instance_pool( + instance_pool_id).data + + return stop_instance_pool_response + + +def stop_all_instance_pools_in_compartment(instance_pool_ids: List[str], + filters: List[Dict[str, Any]], + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> OCIResponse: + """Stop the given OCI Compute instance pool. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InstancePool.html#oci.core.models.Instance + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + if not instance_pool_ids: + logger.warning('Based on configuration provided I am going to ' + 'stop all Instance Pools in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + instance_pools = get_instance_pools(client, compartment_id) + + filters = filters or None + if filters is not None: + instance_pool_ids = filter_instance_pools(instance_pools, filters=filters) + + if not instance_pool_ids: + raise FailedActivity( + 'No Instance Pools found matching filters: %s' % str(filters)) + + logger.debug('Instance Pools in Compartment %s selected: %s}.' % ( + compartment_id, str(instance_pool_ids))) + + stop_instance_pool_response = [] + + for instance_pool_id in instance_pool_ids: + logger.debug("Picked Compute Instance Pool '{}' from Compartment '{}' to be stopped", instance_pool_id, + compartment_id) + + stop_instance_pool_response = stop_instance_pool( + instance_pool_id) + + return stop_instance_pool_response + + +def start_instance_pool(instance_pool_id: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Start the given OCI Compute instances.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + start_instance_pool_response = client.start_instance_pool( + instance_pool_id).data + + return start_instance_pool_response + + +def start_all_instance_pools_in_compartment(instance_pool_ids: List[str], + filters: List[Dict[str, Any]], + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> OCIResponse: + """Start the given OCI Compute instances, If only an Compartment is specified, all instances in + that Compartment will be stopped. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InstancePool.html#oci.core.models.Instance + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + if not instance_pool_ids: + logger.warning('Based on configuration provided I am going to ' + 'Start all Instance Pools in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + instance_pools = get_instance_pools(client, compartment_id) + + filters = filters or None + if filters is not None: + instance_pool_ids = filter_instance_pools(instance_pools, filters=filters) + + if not instance_pool_ids: + raise FailedActivity( + 'No Instance Pools found matching filters: %s' % str(filters)) + + logger.debug('Instance Pools in Compartment %s selected: %s}.' % ( + compartment_id, str(instance_pool_ids))) + + start_instance_pool_response = [] + + for instance_pool_id in instance_pool_ids: + logger.debug("Picked Compute Instance Pool '{}' from Compartment '{}' to be started", instance_pool_id, + compartment_id) + + start_instance_pool_response = start_instance_pool( + instance_pool_id) + + return start_instance_pool_response + + +def terminate_instance_pool(instance_pool_id: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Terminate the given OCI Compute instances.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + terminate_instance_pool_response = client.terminate_instance_pool( + instance_pool_id).data + + return terminate_instance_pool_response + + +def terminate_all_instance_pools_in_compartment(instance_pool_ids: List[str], + filters: List[Dict[str, Any]], + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> OCIResponse: + """Terminate the given OCI Compute instances, If only an Compartment is specified, all instances in + that Compartment will be terminated. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InstancePool.html#oci.core.models.Instance + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + if not instance_pool_ids: + logger.warning('Based on configuration provided I am going to ' + 'Terminate all Instance Pools in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + instance_pools = get_instance_pools(client, compartment_id) + + filters = filters or None + if filters is not None: + instance_pool_ids = filter_instance_pools(instance_pools, filters=filters) + + if not instance_pool_ids: + raise FailedActivity( + 'No Instance Pools found matching filters: %s' % str(filters)) + + logger.debug('Instance Pools in Compartment %s selected: %s}.' % ( + compartment_id, str(instance_pool_ids))) + + terminate_instance_pool_response = [] + + for instance_pool_id in instance_pool_ids: + logger.debug("Picked Compute Instance Pool '{}' from Compartment '{}' to be terminated", instance_pool_id, + compartment_id) + + terminate_instance_pool_response = terminate_instance_pool( + instance_pool_id) + + return terminate_instance_pool_response + + +def reset_instance_pool(instance_pool_id: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Reset the given OCI Compute Instance Pools""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + reset_instance_pool_response = client.reset_instance_pool( + instance_pool_id).data + + return reset_instance_pool_response + + +def reset_all_instance_pools_in_compartment(instance_pool_ids: List[str], + filters: List[Dict[str, Any]], + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> OCIResponse: + """Reset the given OCI Compute Instance Pools, If only an Compartment is specified, all Instance Pools in + that Compartment will be Reset. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InstancePool.html#oci.core.models.Instance + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + if not instance_pool_ids: + logger.warning('Based on configuration provided I am going to ' + 'Reset all Instance Pools in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + instance_pools = get_instance_pools(client, compartment_id) + + filters = filters or None + if filters is not None: + instance_pool_ids = filter_instance_pools(instance_pools, filters=filters) + + if not instance_pool_ids: + raise FailedActivity( + 'No instances found matching filters: %s' % str(filters)) + + logger.debug('Instance Pools in Compartment %s selected: %s}.' % ( + compartment_id, str(instance_pool_ids))) + + reset_instance_pool_response = [] + + for instance_pool_id in instance_pool_ids: + logger.debug("Picked Compute Instance Pool '{}' from Compartment '{}' to be reset", instance_pool_id, + compartment_id) + + reset_instance_pool_response = reset_instance_pool( + instance_pool_id) + + return reset_instance_pool_response + + +def softreset_instance_pool(instance_pool_id: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Soft Reset the given OCI Compute instance pool.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + softreset_instance_pool_response = client.softreset_instance_pool( + instance_pool_id).data + + return softreset_instance_pool_response + + +def softreset_all_instance_pools_in_compartment(instance_pool_ids: List[str], + filters: List[Dict[str, Any]], + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> OCIResponse: + """SoftReset the given OCI Compute Instance Pools, If only an Compartment is specified, all Instance Pools in + that Compartment will be SoftReset. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InstancePool.html#oci.core.models.Instance + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=True) + + if not instance_pool_ids: + logger.warning('Based on configuration provided I am going to ' + 'terminate all Instance Pools in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + instance_pools = get_instance_pools(client, compartment_id) + + filters = filters or None + if filters is not None: + instance_pool_ids = filter_instance_pools(instance_pools, filters=filters) + + if not instance_pool_ids: + raise FailedActivity( + 'No Instance Pools found matching filters: %s' % str(filters)) + + logger.debug('Instance Pools in Compartment %s selected: %s}.' % ( + compartment_id, str(instance_pool_ids))) + + softreset_instance_pool_response = [] + + for instance_pool_id in instance_pool_ids: + logger.debug("Picked Compute Instance Pool '{}' from Compartment '{}' to be stopped", instance_pool_id, + compartment_id) + + softreset_instance_pool_response = softreset_instance_pool( + instance_pool_id) + + return softreset_instance_pool_response diff --git a/chaosoci/core/compute/common.py b/chaosoci/core/compute/common.py index 4dbf5e1..937d683 100644 --- a/chaosoci/core/compute/common.py +++ b/chaosoci/core/compute/common.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__all__ = ["get_instances", "filter_instances"] +__all__ = ["get_instances", "filter_instances", "get_instance_pools", "filter_instance_pools"] from typing import Any, Dict, List @@ -7,8 +7,8 @@ from logzero import logger -from oci.core import ComputeClient -from oci.core.models import Instance +from oci.core import ComputeClient, ComputeManagementClient +from oci.core.models import Instance, InstancePool def get_instances(client: ComputeClient = None, @@ -55,3 +55,49 @@ def filter_instances(instances: List[Instance] = None, filtered.append(instance) return filtered + + +def get_instance_pools(client: ComputeManagementClient = None, + compartment_id: str = None) -> List[InstancePool]: + """Return a complete, unfiltered list of Instance Pools in the compartment.""" + instance_pools = [] + + instances_pool_raw = client.list_instance_pools(compartment_id=compartment_id) + instance_pools.extend(instances_pool_raw.data) + while instances_pool_raw.has_next_page: + instances_pool_raw = client.list_instances(compartment_id=compartment_id, + page=instances_pool_raw.next_page) + instance_pools.extend(instances_pool_raw.data) + + return instance_pools + + +def filter_instance_pools(instance_pools: List[InstancePool] = None, + filters: Dict[str, Any] = None) -> List[InstancePool]: + """Return only those Instance Pools that match the filters provided.""" + instance_pools = instance_pools or None + + if instance_pools is None: + raise ActivityFailed('No Instance Pools were found.') + + filters_set = {x for x in filters} + available_filters_set = {x for x in instance_pools[0].attribute_map} + + # Partial filtering may return instances we do not want. We avoid it. + if not filters_set.issubset(available_filters_set): + raise ActivityFailed('Some of the chosen filters were not found,' + ' we cannot continue.') + + # Walk the instances and find those that match the given filters. + filtered = [] + for instance_pool in instance_pools: + sentinel = True + for attr, val in filters.items(): + if val != getattr(instance_pool, attr, None): + sentinel = False + break + + if sentinel: + filtered.append(instance_pool) + + return filtered diff --git a/chaosoci/core/compute/probes.py b/chaosoci/core/compute/probes.py index 9559ab1..a0f57df 100644 --- a/chaosoci/core/compute/probes.py +++ b/chaosoci/core/compute/probes.py @@ -5,13 +5,13 @@ from chaoslib.types import Configuration, Secrets from oci.config import from_file -from oci.core import ComputeClient +from oci.core import ComputeClient, ComputeManagementClient from chaosoci import oci_client -from .common import filter_instances, get_instances +from .common import filter_instances, get_instances, get_instance_pools -__all__ = ['count_instances'] +__all__ = ['count_instances', 'count_instance_pools'] def count_instances(filters: List[Dict[str, Any]], compartment_id: str = None, @@ -40,3 +40,31 @@ def count_instances(filters: List[Dict[str, Any]], compartment_id: str = None, return len(filter_instances(instances, filters=filters)) return len(instances) + + +def count_instance_pools(filters: List[Dict[str, Any]], compartment_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + """ + Return the number of instance pools in accordance with the given filters. + + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InstancePool.html#oci.core.models.Instance + + for details on the available filters under the 'parameters' section. + """ # noqa: E501 + compartment_id = compartment_id or from_file().get('compartment') + + if compartment_id is None: + raise ActivityFailed('We have not been able to find a compartment,' + ' without one, we cannot continue.') + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + instance_pools = get_instance_pools(client, compartment_id) + + if filters is not None: + return len(filter_instances(instance_pools, filters=filters)) + + return len(instance_pools) diff --git a/chaosoci/core/loadBalancer/__init__.py b/chaosoci/core/loadBalancer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chaosoci/core/loadBalancer/actions.py b/chaosoci/core/loadBalancer/actions.py new file mode 100644 index 0000000..7800c90 --- /dev/null +++ b/chaosoci/core/loadBalancer/actions.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- + +from chaoslib.types import Configuration, Secrets +from oci.load_balancer import LoadBalancerClient + +from chaosoci import oci_client +from chaosoci.types import OCIResponse + +__all__ = ["delete_backend_server", "delete_backend_set", + "delete_hostname", "delete_listener", + "delete_load_balancer", "delete_routing_policy", + "delete_path_route_set"] + + +# Compute Client Actions + +def delete_backend_server(load_balancer_id: str, + backend_set_name: str, + backend_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given backend server""" + + client = oci_client(LoadBalancerClient, configuration, secrets, + skip_deserialization=True) + delete_backend_response = client.delete_backend(load_balancer_id, backend_name, backend_set_name).data + + return delete_backend_response + + +def delete_backend_set(load_balancer_id: str, + backend_set_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given backend set""" + client = oci_client(LoadBalancerClient, configuration, secrets, + skip_deserialization=False) + + delete_backend_set_response = client.delete_backend_set(load_balancer_id, backend_set_name).data + + return delete_backend_set_response + + +# Compute Client Management Actions + +def delete_hostname(load_balancer_id: str, + load_balancer_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given hostname""" + + client = oci_client(LoadBalancerClient, configuration, secrets, + skip_deserialization=True) + + delete_hostname_response = client.delete_hostname(load_balancer_id, load_balancer_name).data + + return delete_hostname_response + + +def delete_listener(listener_id: str, + listener_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given LB Listener""" + + client = oci_client(LoadBalancerClient, configuration, secrets, + skip_deserialization=True) + + delete_listener_response = client.delete_listener(listener_id, listener_name).data + + return delete_listener_response + + +def delete_load_balancer(load_balancer_id: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given Load Balancer""" + + client = oci_client(LoadBalancerClient, configuration, secrets, + skip_deserialization=True) + + delete_load_balancer_response = client.delete_load_balancer(load_balancer_id).data + + return delete_load_balancer_response + + +def delete_path_route_set(load_balancer_id: str, + path_route_set_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given set path route""" + + client = oci_client(LoadBalancerClient, configuration, secrets, + skip_deserialization=True) + + delete_path_route_set_response = client.delete_path_route_set(load_balancer_id, path_route_set_name).data + + return delete_path_route_set_response + + +def delete_routing_policy(load_balancer_id: str, + routing_policy_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given routing policy""" + + client = oci_client(LoadBalancerClient, configuration, secrets, + skip_deserialization=True) + + delete_routing_policy_response = client.delete_routing_policy(load_balancer_id, routing_policy_name).data + + return delete_routing_policy_response diff --git a/chaosoci/core/loadBalancer/common.py b/chaosoci/core/loadBalancer/common.py new file mode 100644 index 0000000..4fcb2b0 --- /dev/null +++ b/chaosoci/core/loadBalancer/common.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +__all__ = ["get_load_balancers", "filter_load_balancers", "get_backend_sets"] + +from typing import Any, Dict, List + +from chaoslib.exceptions import ActivityFailed + +from logzero import logger + +from oci.core import ComputeClient, ComputeManagementClient +from oci.core.models import Instance, InstancePool +from oci.load_balancer import LoadBalancerClient +from oci.load_balancer.models import LoadBalancer + + +def get_load_balancers(client: LoadBalancerClient = None, + compartment_id: str = None) -> List[Instance]: + """Return a complete, unfiltered list of instances in the compartment.""" + load_bals = [] + + load_bals_raw = client.list_load_balancers(compartment_id=compartment_id) + load_bals.extend(load_bals_raw.data) + while load_bals_raw.has_next_page: + load_bals_raw = client.list_load_balancers(compartment_id=compartment_id, + page=load_bals_raw.next_page) + load_bals.extend(load_bals_raw.data) + + return load_bals + + +def filter_load_balancers(load_bals: List[LoadBalancer] = None, + filters: Dict[str, Any] = None) -> List[LoadBalancer]: + """Return only those load_bals that match the filters provided.""" + load_bals = load_bals or None + + if load_bals is None: + raise ActivityFailed('No load_bals were found.') + + filters_set = {x for x in filters} + available_filters_set = {x for x in load_bals[0].attribute_map} + + # Partial filtering may return load_bals we do not want. We avoid it. + if not filters_set.issubset(available_filters_set): + raise ActivityFailed('Some of the chosen filters were not found,' + ' we cannot continue.') + + # Walk the load_bals and find those that match the given filters. + filtered = [] + for load_bal in load_bals: + sentinel = True + for attr, val in filters.items(): + if val != getattr(load_bal, attr, None): + sentinel = False + break + + if sentinel: + filtered.append(load_bal) + + return filtered + + +def get_backend_sets(client: LoadBalancerClient = None, + loadbalancer_id: str = None) -> List[Instance]: + """Return a complete, unfiltered list of instances in the compartment.""" + backend_set = [] + + backend_set_raw = client.list_backend_sets(load_balancer_id=loadbalancer_id) + backend_set.extend(backend_set_raw.data) + while backend_set_raw.has_next_page: + backend_set_raw = client.list_backend_sets(load_balancer_id=loadbalancer_id, + page=backend_set_raw.next_page) + backend_set.extend(backend_set_raw.data) + + return backend_set diff --git a/chaosoci/core/loadBalancer/probes.py b/chaosoci/core/loadBalancer/probes.py new file mode 100644 index 0000000..d8e892f --- /dev/null +++ b/chaosoci/core/loadBalancer/probes.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +from typing import Any, Dict, List + +from chaoslib.exceptions import ActivityFailed +from chaoslib.types import Configuration, Secrets + +from oci.config import from_file +from oci.core import ComputeClient, ComputeManagementClient + +from chaosoci import oci_client + +from .common import get_load_balancers, get_backend_sets, filter_load_balancers + +__all__ = ['count_load_bal', 'count_backend_sets'] + + +def count_load_bal(filters: List[Dict[str, Any]], compartment_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + """ + Return the number of instances in accordance with the given filters. + + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.Instance.html#oci.core.models.Instance + + for details on the available filters under the 'parameters' section. + """ # noqa: E501 + compartment_id = compartment_id or from_file().get('compartment') + + if compartment_id is None: + raise ActivityFailed('We have not been able to find a compartment,' + ' without one, we cannot continue.') + + client = oci_client(ComputeClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + instances = get_load_balancers(client, compartment_id) + + if filters is not None: + return len(filter_load_balancers(instances, filters=filters)) + + return len(instances) + + +def count_backend_sets(filters: List[Dict[str, Any]], loadbalancer_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + + loadbalancer_id = loadbalancer_id or from_file().get('load_balancer') + + if loadbalancer_id is None: + raise ActivityFailed('We have not been able to find a compartment,' + ' without one, we cannot continue.') + + client = oci_client(ComputeClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + backend_sets = get_backend_sets(client, loadbalancer_id) + + if filters is not None: + return len(filter_load_balancers(backend_sets, filters=filters)) + + return len(backend_sets) diff --git a/chaosoci/core/networking/actions.py b/chaosoci/core/networking/actions.py index 5f0007f..3521877 100644 --- a/chaosoci/core/networking/actions.py +++ b/chaosoci/core/networking/actions.py @@ -1,13 +1,17 @@ # coding: utf-8 # Copyright 2020, Oracle Corporation and/or its affiliates. -__all__ = ["delete_route_table_by_id", "delete_route_table_by_filters"] +__all__ = ["delete_route_table_by_id", "delete_route_table_by_filters", + "delete_nat_gateway_by_id", "delete_nat_gateway_by_filters", + "delete_internet_gateway_by_id", "delete_internet_gateway_by_filters", + "delete_service_gateway_by_id", "delete_service_gateway_by_filters"] from random import choice from typing import Any, Dict, List from chaoslib.exceptions import ActivityFailed from chaoslib.types import Configuration, Secrets +from oci.retry import DEFAULT_RETRY_STRATEGY from chaosoci import oci_client from chaosoci.types import OCIResponse @@ -18,9 +22,9 @@ from oci.config import from_file from oci.core import VirtualNetworkClient -from .common import (get_route_tables) +from .common import (get_route_tables, get_service_gateway, get_internet_gateway, get_nat_gateway) -from .filters import (filter_route_tables) +from .filters import (filter_route_tables, filter_nat_gateway, filter_internet_gateway, filter_service_gateway) def delete_route_table_by_id(rt_id: str, force: bool = False, @@ -46,6 +50,7 @@ def delete_route_table_by_id(rt_id: str, force: bool = False, def delete_route_table_by_filters(compartment_id: str, vcn_id: str, filters: Dict[str, Any], force: bool = False, + retry_strategy=None, configuration: Configuration = None, secrets: Secrets = None) -> OCIResponse: """ @@ -75,10 +80,208 @@ def delete_route_table_by_filters(compartment_id: str, vcn_id: str, else: filtered = filter_route_tables(unfiltered, filters) - if (len(filtered) == 0): + if len(filtered) == 0: raise ActivityFailed(FILTER_ERR) else: - ret = client.delete_route_table(filtered[0].id).data + if not retry_strategy: + retry_strategy = DEFAULT_RETRY_STRATEGY + + ret = client.delete_route_table(filtered[0].id, retry_strategy).data logger.debug("Route table %s deleted", filtered[0].display_name) return ret + + +def delete_nat_gateway_by_id(nw_id: str, force: bool = False, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """ + Deletes a given Nat Gateway using the Nat Gateway id. + + Parameters: + Required: + - nw_id: the id of the Nat Gateway + """ + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=True) + if not nw_id: + raise ActivityFailed('A Nat Gateway id is required.') + + ret = client.delete_nat_gateway(nw_id=nw_id).data + logger.debug("Nat Gateway %s deleted", nw_id) + return ret + + +def delete_nat_gateway_by_filters(compartment_id: str, vcn_id: str, + filters: Dict[str, Any], force: bool = False, + retry_strategy=None, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """ + Search for a Nat Gateway in VCN using the specified filters and + then deletes it. + + Parameters: + Required: + - compartment_id: the compartment id of the VCN + - vcn_id: the id of the VCN + - filters: the set of filters for the Nat Gateway. + Please refer to + https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.NatGateway.html# + for the Nat Gateway filters. + """ + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=False) + + if compartment_id is None or vcn_id is None: + raise ActivityFailed('A compartment id or vcn id is required.') + else: + unfiltered = get_nat_gateway(client, compartment_id, vcn_id) + + if filters is None: + raise ActivityFailed(FILTER_ERR) + else: + filtered = filter_nat_gateway(unfiltered, filters) + + if len(filtered) == 0: + raise ActivityFailed(FILTER_ERR) + else: + if not retry_strategy: + retry_strategy = DEFAULT_RETRY_STRATEGY + + ret = client.delete_nat_gateway(filtered[0].id, retry_strategy).data + logger.debug("Nat Gateway %s deleted", + filtered[0].display_name) + return ret + + +def delete_internet_gateway_by_id(nw_id: str, force: bool = False, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """ + Deletes a given Internet Gateway using the Internet Gateway id. + + Parameters: + Required: + - nw_id: the id of the Internet Gateway + """ + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=True) + if not nw_id: + raise ActivityFailed('A Internet Gateway id is required.') + + ret = client.delete_internet_gateway(ig_id=nw_id).data + logger.debug("Internet Gateway %s deleted", nw_id) + return ret + + +def delete_internet_gateway_by_filters(compartment_id: str, vcn_id: str, + filters: Dict[str, Any], force: bool = False, + retry_strategy=None, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """ + Search for a Internet Gateway in VCN using the specified filters and + then deletes it. + + Parameters: + Required: + - compartment_id: the compartment id of the VCN + - vcn_id: the id of the VCN + - filters: the set of filters for the Internet Gateway. + Please refer to + https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InternetGateway.html# + for the Internet Gateway filters. + """ + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=False) + + if compartment_id is None or vcn_id is None: + raise ActivityFailed('A compartment id or vcn id is required.') + else: + unfiltered = get_internet_gateway(client, compartment_id, vcn_id) + + if filters is None: + raise ActivityFailed(FILTER_ERR) + else: + filtered = filter_internet_gateway(unfiltered, filters) + + if len(filtered) == 0: + raise ActivityFailed(FILTER_ERR) + else: + if not retry_strategy: + retry_strategy = DEFAULT_RETRY_STRATEGY + + ret = client.delete_internet_gateway(filtered[0].id, retry_strategy).data + logger.debug("Internet Gateway %s deleted", + filtered[0].display_name) + return ret + + +def delete_service_gateway_by_id(nw_id: str, force: bool = False, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """ + Deletes a given Service Gateway using the Service Gateway id. + + Parameters: + Required: + - nw_id: the id of the Service Gateway + """ + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=True) + if not nw_id: + raise ActivityFailed('A Service Gateway id is required.') + + ret = client.delete_service_gateway(sg_id=nw_id).data + logger.debug("Service Gateway %s deleted", nw_id) + return ret + + +def delete_service_gateway_by_filters(compartment_id: str, vcn_id: str, + filters: Dict[str, Any], force: bool = False, + retry_strategy=None, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """ + Search for a Service Gateway in VCN using the specified filters and + then deletes it. + + Parameters: + Required: + - compartment_id: the compartment id of the VCN + - vcn_id: the id of the VCN + - filters: the set of filters for the Service Gateway. + Please refer to + https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.ServiceGateway.html# + for the Service Gateway filters. + """ + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=False) + + if compartment_id is None or vcn_id is None: + raise ActivityFailed('A compartment id or vcn id is required.') + else: + unfiltered = get_service_gateway(client, compartment_id, vcn_id) + + if filters is None: + raise ActivityFailed(FILTER_ERR) + else: + filtered = filter_service_gateway(unfiltered, filters) + + if len(filtered) == 0: + raise ActivityFailed(FILTER_ERR) + else: + if not retry_strategy: + retry_strategy = DEFAULT_RETRY_STRATEGY + + ret = client.delete_service_gateway(filtered[0].id, retry_strategy).data + logger.debug("Service Gateway %s deleted", + filtered[0].display_name) + return ret diff --git a/chaosoci/core/networking/common.py b/chaosoci/core/networking/common.py index 930fda0..ad2f0bc 100644 --- a/chaosoci/core/networking/common.py +++ b/chaosoci/core/networking/common.py @@ -1,7 +1,7 @@ # coding: utf-8 # Copyright 2020, Oracle Corporation and/or its affiliates. -__all__ = ["get_route_table", "get_route_tables"] +__all__ = ["get_nat_gateway", "get_route_tables", "get_internet_gateway", "get_service_gateway"] from typing import Any, Dict, List @@ -21,7 +21,6 @@ def get_route_tables(client: VirtualNetworkClient = None, Returns a complete, unfiltered list of route tables of a vcn in the compartment. """ - route_tables = [] route_tables_raw = client.list_route_tables(compartment_id=compartment_id, vcn_id=vcn_id) @@ -34,3 +33,66 @@ def get_route_tables(client: VirtualNetworkClient = None, route_tables.extend(route_tables_raw.data) return route_tables + + +def get_nat_gateway(client: VirtualNetworkClient = None, + compartment_id: str = None, + vcn_id: str = None) -> List[RouteTable]: + """ + Returns a complete, unfiltered list of Nat Gateways of a vcn in the + compartment. + """ + nat_gateway = [] + nat_gateway_raw = client.list_nat_gateways(compartment_id=compartment_id, + vcn_id=vcn_id) + nat_gateway.extend(nat_gateway_raw.data) + while nat_gateway_raw.has_next_page: + nat_gateway_raw = client.list_nat_gateways( + compartment_id=compartment_id, + vcn_id=vcn_id, + page=nat_gateway_raw.next_page) + nat_gateway.extend(nat_gateway_raw.data) + + return nat_gateway + + +def get_internet_gateway(client: VirtualNetworkClient = None, + compartment_id: str = None, + vcn_id: str = None) -> List[RouteTable]: + """ + Returns a complete, unfiltered list of Internet Gateways of a vcn in the + compartment. + """ + internet_gateway = [] + internet_gateway_raw = client.list_internet_gateways(compartment_id=compartment_id, + vcn_id=vcn_id) + internet_gateway.extend(internet_gateway_raw.data) + while internet_gateway_raw.has_next_page: + internet_gateway_raw = client.list_internet_gateways( + compartment_id=compartment_id, + vcn_id=vcn_id, + page=internet_gateway_raw.next_page) + internet_gateway.extend(internet_gateway_raw.data) + + return internet_gateway + + +def get_service_gateway(client: VirtualNetworkClient = None, + compartment_id: str = None, + vcn_id: str = None) -> List[RouteTable]: + """ + Returns a complete, unfiltered list of Service Gateways of a vcn in the + compartment. + """ + service_gateway = [] + service_gateway_raw = client.list_service_gateways(compartment_id=compartment_id, + vcn_id=vcn_id) + service_gateway.extend(service_gateway_raw.data) + while service_gateway_raw.has_next_page: + service_gateway_raw = client.list_service_gateways( + compartment_id=compartment_id, + vcn_id=vcn_id, + page=service_gateway_raw.next_page) + service_gateway.extend(service_gateway_raw.data) + + return service_gateway diff --git a/chaosoci/core/networking/filters.py b/chaosoci/core/networking/filters.py index b116da6..1c34ff6 100644 --- a/chaosoci/core/networking/filters.py +++ b/chaosoci/core/networking/filters.py @@ -1,7 +1,7 @@ # coding: utf-8 # Copyright 2020, Oracle Corporation and/or its affiliates. -__all__ = ["filter_route_tables"] +__all__ = ["filter_route_tables", "filter_nat_gateway", "filter_service_gateway", "filter_internet_gateway"] from typing import Any, Dict, List @@ -11,8 +11,7 @@ from logzero import logger from oci.core import VirtualNetworkClient -from oci.core.models import (RouteRule, - RouteTable) +from oci.core.models import (RouteTable, NatGateway, InternetGateway, ServiceGateway) def filter_route_tables(route_tables: List[RouteTable] = None, @@ -20,29 +19,58 @@ def filter_route_tables(route_tables: List[RouteTable] = None, """ Return only those route tables that match the filters provided. """ - route_tables = route_tables or None + return filter_networks("Route Tables", route_tables, filters) - if route_tables is None: - raise ActivityFailed('No route tables were found.') + +def filter_nat_gateway(nat_gateways: List[NatGateway] = None, + filters: Dict[str, Any] = None) -> List[NatGateway]: + """ + Return only those network gateways that match the filters provided. + """ + return filter_networks("Nat Gateway", nat_gateways, filters) + + +def filter_internet_gateway(internet_gateways: List[InternetGateway] = None, + filters: Dict[str, Any] = None) -> List[InternetGateway]: + """ + Return only those internet gateways that match the filters provided. + """ + return filter_networks("Internet Gateway", internet_gateways, filters) + + +def filter_service_gateway(service_gateways: List[ServiceGateway] = None, + filters: Dict[str, Any] = None) -> List[ServiceGateway]: + """ + Return only those service gateways that match the filters provided. + """ + + return filter_networks("Service Gateway", service_gateways, filters) + + +def filter_networks(gateway_type, gateways, filters): + gateways = gateways or None + + if gateways is None: + raise ActivityFailed('No {} were found.', gateway_type) filters_set = {x for x in filters} - available_filters_set = {x for x in route_tables[0].attribute_map} + available_filters_set = {x for x in gateways[0].attribute_map} - # Partial filtering may return route tables we do not want. We avoid it. + # Partial filtering may return service gateways we do not want. We avoid it. if not filters_set.issubset(available_filters_set): raise ActivityFailed(FILTER_ERR) - # Walk the route tables and find those that match the given filters. + # Walk the service gateways and find those that match the given filters. filtered = [] - for route_table in route_tables: + for service_gateway in gateways: sentinel = True for attr, val in filters.items(): - if val != getattr(route_table, attr, None): + if val != getattr(service_gateway, attr, None): sentinel = False break if sentinel: - filtered.append(route_table) + filtered.append(service_gateway) return filtered diff --git a/chaosoci/core/networking/probes.py b/chaosoci/core/networking/probes.py index cd0ee5a..7b22319 100644 --- a/chaosoci/core/networking/probes.py +++ b/chaosoci/core/networking/probes.py @@ -1,7 +1,7 @@ # coding: utf-8 # Copyright 2020, Oracle Corporation and/or its affiliates. -__all__ = ['count_route_tables'] +__all__ = ['count_route_tables', 'count_nat_gateway', 'count_service_gateway', 'count_internet_gateway'] from typing import Any, Dict, List @@ -15,9 +15,9 @@ from oci.config import from_file from oci.core import VirtualNetworkClient -from .common import (get_route_tables) +from .common import (get_route_tables, get_nat_gateway, get_internet_gateway, get_service_gateway) -from .filters import (filter_route_tables) +from .filters import (filter_route_tables, filter_nat_gateway, filter_internet_gateway, filter_service_gateway) def count_route_tables(filters: List[Dict[str, Any]], @@ -47,3 +47,90 @@ def count_route_tables(filters: List[Dict[str, Any]], return len(filter_route_tables(route_tables, filters=filters)) else: return len(route_tables) + + +def count_nat_gateway(filters: List[Dict[str, Any]], + compartment_id: str = None, + vcn_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + """ + Returns the number of Nat Gateways in the compartment 'compartment_id' + and vcn 'vcn_id' and according to the given filters. + + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.RouteTable.html# + + for details on the available filters under the 'parameters' section. + """ # noqa: E501 + compartment_id = compartment_id or from_file().get('compartment') + + if compartment_id is None: + raise ActivityFailed('A valid compartment id is required.') + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + nat_gateway = get_nat_gateway(client, compartment_id, vcn_id) + if filters is not None: + return len(filter_nat_gateway(nat_gateway, filters=filters)) + else: + return len(nat_gateway) + + +def count_internet_gateway(filters: List[Dict[str, Any]], + compartment_id: str = None, + vcn_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + """ + Returns the number of Internet Gateways in the compartment 'compartment_id' + and vcn 'vcn_id' and according to the given filters. + + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.RouteTable.html# + + for details on the available filters under the 'parameters' section. + """ # noqa: E501 + compartment_id = compartment_id or from_file().get('compartment') + + if compartment_id is None: + raise ActivityFailed('A valid compartment id is required.') + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + internet_gateway = get_internet_gateway(client, compartment_id, vcn_id) + if filters is not None: + return len(filter_internet_gateway(internet_gateway, filters=filters)) + else: + return len(internet_gateway) + + +def count_service_gateway(filters: List[Dict[str, Any]], + compartment_id: str = None, + vcn_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + """ + Returns the number of Service Gateways in the compartment 'compartment_id' + and vcn 'vcn_id' and according to the given filters. + + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.RouteTable.html# + + for details on the available filters under the 'parameters' section. + """ # noqa: E501 + compartment_id = compartment_id or from_file().get('compartment') + + if compartment_id is None: + raise ActivityFailed('A valid compartment id is required.') + + client = oci_client(VirtualNetworkClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + service_gateway = get_service_gateway(client, compartment_id, vcn_id) + if filters is not None: + return len(filter_service_gateway(service_gateway, filters=filters)) + else: + return len(service_gateway) diff --git a/chaosoci/core/objectStorage/__init__.py b/chaosoci/core/objectStorage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chaosoci/core/objectStorage/actions.py b/chaosoci/core/objectStorage/actions.py new file mode 100644 index 0000000..b81304c --- /dev/null +++ b/chaosoci/core/objectStorage/actions.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +from collections import defaultdict +from random import choice +from typing import Any, Dict, List + +import oci +from chaoslib.exceptions import ActivityFailed, FailedActivity +from chaoslib.types import Configuration, Secrets +from oci.load_balancer import LoadBalancerClient +from oci.object_storage import ObjectStorageClient +from oci.retry import DEFAULT_RETRY_STRATEGY + +from chaosoci import oci_client +from chaosoci.types import OCIResponse + +from logzero import logger + +from oci.config import from_file +from oci.core import ComputeClient, ComputeManagementClient + +from .common import (get_buckets, filter_buckets, get_objects, filter_obstore_objects) + +__all__ = ["delete_bucket", "delete_buckets_in_compartment", + "delete_object", "delete_objects_in_compartment"] + + +# Compute Client Actions + +def delete_bucket(namespace_name: str, + bucket_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given Bucket""" + + client = oci_client(ObjectStorageClient, configuration, secrets, + skip_deserialization=True) + delete_bucket_response = client.delete_bucket(namespace_name, bucket_name).data + + return delete_bucket_response + + +def delete_buckets_in_compartment(filters: List[Dict[str, Any]], + namespace_name: str, + bucket_names: List[str] = None, + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> OCIResponse: + """Delete the given OCI bucket, If only an Compartment is specified, all buckets in + that Compartment will be deleted. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/object_storage/models/oci.object_storage.models.Bucket.html#oci.object_storage.models.Bucket + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ObjectStorageClient, configuration, secrets, + skip_deserialization=True) + if not bucket_names: + logger.warning('Based on configuration provided I am going to ' + 'delete all buckets in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + bucket_names = get_buckets(client, compartment_id) + + filters = filters or None + if filters is not None: + bucket_names = filter_buckets(bucket_names, filters=filters) + + if not bucket_names: + raise FailedActivity( + 'No buckets found matching filters: %s' % str(filters)) + + logger.debug('Buckets in Compartment %s selected: %s}.' % ( + compartment_id, str(bucket_names))) + + delete_bucket_response = [] + + for bucket_name in bucket_names: + logger.debug("Picked bucket '{}' from Compartment '{}' to be deleted", bucket_name, compartment_id) + + delete_bucket_response.append(delete_bucket(namespace_name, bucket_name)) + + return delete_bucket_response + + +def delete_object(namespace_name: str, + bucket_name: str, + object_name: str, + configuration: Configuration = None, + secrets: Secrets = None) -> OCIResponse: + """Delete a given object""" + + client = oci_client(ObjectStorageClient, configuration, secrets, + skip_deserialization=True) + delete_object_response = client.delete_object(namespace_name, bucket_name, object_name).data + + return delete_object_response + + +def delete_objects_in_compartment(filters: List[Dict[str, Any]], + namespace_name: str, + bucket_name: str, + object_names: List[str] = None, + configuration: Configuration = None, + compartment_id: str = None, + secrets: Secrets = None) -> List[Dict[str, Any]]: + """Delete the given OCI Object, If only an Compartment is specified, all objects in + that Compartment will be deleted. If you need more control, you can + also provide a list of filters following the documentation. + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/object_storage/models/oci.object_storage.models.ObjectSummary.html#oci.object_storage.models.ObjectSummary + for details on the available filters under the 'parameters' section.""" + + client = oci_client(ObjectStorageClient, configuration, secrets, + skip_deserialization=True) + if not object_names: + logger.warning('Based on configuration provided I am going to ' + 'delete all objects in the Compartment %s! matching the filter criteria' + % compartment_id) + + compartment_id = compartment_id or from_file().get('compartment') + object_names = get_objects(client, compartment_id) + + filters = filters or None + if filters is not None: + object_names = filter_obstore_objects(object_names, filters=filters) + + if not object_names: + raise FailedActivity( + 'No objects found matching filters: %s' % str(filters)) + + logger.debug('Objects in Compartment %s selected: %s}.' % ( + compartment_id, str(object_names))) + + delete_object_response = [] + + for object_name in object_names: + logger.debug("Picked Object '{}' from Compartment '{}' to be deleted", bucket_name, compartment_id) + + delete_object_response.append(delete_object(namespace_name, bucket_name, object_name)) + + return delete_object_response diff --git a/chaosoci/core/objectStorage/common.py b/chaosoci/core/objectStorage/common.py new file mode 100644 index 0000000..e85d244 --- /dev/null +++ b/chaosoci/core/objectStorage/common.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +__all__ = ["get_buckets", "filter_buckets", "get_objects", "filter_obstore_objects"] + +from typing import Any, Dict, List + +from chaoslib.exceptions import ActivityFailed + +from logzero import logger + +from oci.core import ComputeClient, ComputeManagementClient +from oci.object_storage.models import Bucket, ObjectSummary + +from oci.object_storage import ObjectStorageClient + + +def get_buckets(client: ObjectStorageClient = None, + compartment_id: str = None) -> List[Bucket]: + """Return a complete, unfiltered list of buckets in the compartment.""" + buckets = [] + + buckets_raw = client.list_buckets(compartment_id=compartment_id) + buckets.extend(buckets_raw.data) + while buckets_raw.has_next_page: + buckets_raw = client.list_buckets(compartment_id=compartment_id, + page=buckets_raw.next_page) + buckets.extend(buckets_raw.data) + + return buckets + + +def filter_buckets(buckets: List[Bucket] = None, + filters: Dict[str, Any] = None) -> List[Bucket]: + """Return only those buckets that match the filters provided.""" + buckets = buckets or None + + if buckets is None: + raise ActivityFailed('No buckets were found.') + + filters_set = {x for x in filters} + available_filters_set = {x for x in buckets[0].attribute_map} + + # Partial filtering may return buckets we do not want. We avoid it. + if not filters_set.issubset(available_filters_set): + raise ActivityFailed('Some of the chosen filters were not found,' + ' we cannot continue.') + + # Walk the buckets and find those that match the given filters. + filtered = [] + for bucket in buckets: + sentinel = True + for attr, val in filters.items(): + if val != getattr(bucket, attr, None): + sentinel = False + break + + if sentinel: + filtered.append(bucket) + + return filtered + + +def get_objects(client: ObjectStorageClient = None, + compartment_id: str = None) -> List[ObjectSummary]: + """Return a complete, unfiltered list of instances in the compartment.""" + objects = [] + + objects_raw = client.list_objects(compartment_id=compartment_id) + objects.extend(objects_raw.data) + while objects_raw.has_next_page: + objects_raw = client.list_objects(compartment_id=compartment_id, + page=objects_raw.next_page) + objects.extend(objects_raw.data) + + return objects + + +def filter_obstore_objects(objects: List[ObjectSummary] = None, + filters: Dict[str, Any] = None) -> List[ObjectSummary]: + """Return only those instances that match the filters provided.""" + objects = objects or None + + if objects is None: + raise ActivityFailed('No instances were found.') + + filters_set = {x for x in filters} + available_filters_set = {x for x in objects[0].attribute_map} + + # Partial filtering may return instances we do not want. We avoid it. + if not filters_set.issubset(available_filters_set): + raise ActivityFailed('Some of the chosen filters were not found,' + ' we cannot continue.') + + # Walk the instances and find those that match the given filters. + filtered = [] + for bucket in objects: + sentinel = True + for attr, val in filters.items(): + if val != getattr(bucket, attr, None): + sentinel = False + break + + if sentinel: + filtered.append(bucket) + + return filtered \ No newline at end of file diff --git a/chaosoci/core/objectStorage/probes.py b/chaosoci/core/objectStorage/probes.py new file mode 100644 index 0000000..49151f8 --- /dev/null +++ b/chaosoci/core/objectStorage/probes.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +from typing import Any, Dict, List + +from chaoslib.exceptions import ActivityFailed +from chaoslib.types import Configuration, Secrets + +from oci.config import from_file +from oci.core import ComputeClient, ComputeManagementClient + +from chaosoci import oci_client + +__all__ = ['count_buckets', 'count_objects'] + +from chaosoci.core.objectStorage.common import get_buckets, filter_buckets, filter_obstore_objects, get_objects + + +def count_buckets(filters: List[Dict[str, Any]], compartment_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + """ + Return the number of buckets in accordance with the given filters. + + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.Instance.html#oci.core.models.Instance + + for details on the available filters under the 'parameters' section. + """ # noqa: E501 + compartment_id = compartment_id or from_file().get('compartment') + + if compartment_id is None: + raise ActivityFailed('We have not been able to find a compartment,' + ' without one, we cannot continue.') + + client = oci_client(ComputeClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + buckets = get_buckets(client, compartment_id) + + if filters is not None: + return len(filter_buckets(buckets, filters=filters)) + + return len(buckets) + + +def count_objects(filters: List[Dict[str, Any]], compartment_id: str = None, + configuration: Configuration = None, + secrets: Secrets = None) -> int: + """ + Return the number of objects in accordance with the given filters. + + Please refer to: https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/core/models/oci.core.models.InstancePool.html#oci.core.models.Instance + + for details on the available filters under the 'parameters' section. + """ # noqa: E501 + compartment_id = compartment_id or from_file().get('compartment') + + if compartment_id is None: + raise ActivityFailed('We have not been able to find a compartment,' + ' without one, we cannot continue.') + + client = oci_client(ComputeManagementClient, configuration, secrets, + skip_deserialization=False) + + filters = filters or None + objects = get_objects(client, compartment_id) + + if filters is not None: + return len(filter_obstore_objects(objects, filters=filters)) + + return len(objects) diff --git a/tests/core/LoadBalancer/test_LoadBalancer_actions.py b/tests/core/LoadBalancer/test_LoadBalancer_actions.py new file mode 100644 index 0000000..dccc92f --- /dev/null +++ b/tests/core/LoadBalancer/test_LoadBalancer_actions.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +from unittest.mock import MagicMock, patch + +from chaosoci.core.loadBalancer.actions import delete_backend_server, delete_backend_set, \ + delete_hostname, delete_listener, \ + delete_load_balancer, delete_routing_policy, \ + delete_path_route_set + + +@patch('chaosoci.core.loadBalancer.actions.oci_client', autospec=True) +def test_delete_backend_server(oci_client): + lb_client = MagicMock() + oci_client.return_value = lb_client + load_balancer_id = "lb-id" + backend_set_name = "bes-name" + backend_name = "be-name" + delete_backend_server(load_balancer_id, backend_set_name, backend_name) + lb_client.delete_backend.assert_called_with(load_balancer_id, backend_name, backend_set_name) + + +@patch('chaosoci.core.loadBalancer.actions.oci_client', autospec=True) +def test_delete_backend_set(oci_client): + lb_client = MagicMock() + oci_client.return_value = lb_client + load_balancer_id = "lb-id" + backend_set_name = "bes-name" + delete_backend_set(load_balancer_id, backend_set_name) + lb_client.delete_backend_set.assert_called_with(load_balancer_id, backend_set_name) + + +@patch('chaosoci.core.loadBalancer.actions.oci_client', autospec=True) +def test_delete_hostname(oci_client): + lb_client = MagicMock() + oci_client.return_value = lb_client + load_balancer_id = "lb-id" + load_balancer_name = "bes-name" + delete_hostname(load_balancer_id, load_balancer_name) + lb_client.delete_hostname.assert_called_with(load_balancer_id, load_balancer_name) + + +@patch('chaosoci.core.loadBalancer.actions.oci_client', autospec=True) +def test_delete_listener(oci_client): + lb_client = MagicMock() + oci_client.return_value = lb_client + listener_id = "lb-id" + listener_name = "bes-name" + delete_listener(listener_id, listener_name) + lb_client.delete_listener.assert_called_with(listener_id, listener_name) + + +@patch('chaosoci.core.loadBalancer.actions.oci_client', autospec=True) +def test_delete_load_balancer(oci_client): + lb_client = MagicMock() + oci_client.return_value = lb_client + load_balancer_id = "lb-id" + delete_load_balancer(load_balancer_id) + lb_client.delete_load_balancer.assert_called_with(load_balancer_id) + + +@patch('chaosoci.core.loadBalancer.actions.oci_client', autospec=True) +def test_delete_path_route_set(oci_client): + lb_client = MagicMock() + oci_client.return_value = lb_client + load_balancer_id = "lb-id" + path_route_set_name = "bes-name" + delete_path_route_set(load_balancer_id, path_route_set_name) + lb_client.delete_path_route_set.assert_called_with(load_balancer_id, path_route_set_name) + + +@patch('chaosoci.core.loadBalancer.actions.oci_client', autospec=True) +def test_delete_routing_policy(oci_client): + lb_client = MagicMock() + oci_client.return_value = lb_client + load_balancer_id = "lb-id" + routing_policy_name = "bes-name" + delete_routing_policy(load_balancer_id, routing_policy_name) + lb_client.delete_routing_policy.assert_called_with(load_balancer_id, routing_policy_name) diff --git a/tests/core/LoadBalancer/test_LoadBalancer_probes.py b/tests/core/LoadBalancer/test_LoadBalancer_probes.py new file mode 100644 index 0000000..37f6c17 --- /dev/null +++ b/tests/core/LoadBalancer/test_LoadBalancer_probes.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from unittest import TestCase as T +from unittest.mock import MagicMock, patch + +from chaosoci.core.loadBalancer.probes import count_load_bal, count_backend_sets + + +@patch('chaosoci.core.loadBalancer.probes.filter_load_balancers', autospec=True) +@patch('chaosoci.core.loadBalancer.probes.get_load_balancers', autospec=True) +@patch('chaosoci.core.loadBalancer.probes.oci_client', autospec=True) +def test_count_load_bal(oci_client, get_load_balancers, filter_load_balancers): + lb_client = MagicMock() + oci_client.return_value = lb_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + count_load_bal(filters=filters, compartment_id=c_id) + filter_load_balancers.assert_called_with(get_load_balancers(oci_client, c_id), + filters) + + +@patch('chaosoci.core.loadBalancer.probes.filter_load_balancers', autospec=True) +@patch('chaosoci.core.loadBalancer.probes.get_backend_sets', autospec=True) +@patch('chaosoci.core.loadBalancer.probes.oci_client', autospec=True) +def test_count_count_backend_sets(oci_client, get_backend_sets, filter_load_balancers): + lb_client = MagicMock() + oci_client.return_value = lb_client + + lb_id = "lb-id" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + count_backend_sets(filters=filters, loadbalancer_id=lb_id) + filter_load_balancers.assert_called_with(get_backend_sets(oci_client, lb_id), + filters) diff --git a/tests/core/ObjectStorage/test_objectStorage_actions.py b/tests/core/ObjectStorage/test_objectStorage_actions.py new file mode 100644 index 0000000..4424579 --- /dev/null +++ b/tests/core/ObjectStorage/test_objectStorage_actions.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +from unittest.mock import MagicMock, patch + +from chaosoci.core.objectStorage.actions import delete_bucket, delete_buckets_in_compartment, \ + delete_object, delete_objects_in_compartment + + +@patch('chaosoci.core.objectStorage.actions.oci_client', autospec=True) +def test_delete_bucket(oci_client): + objectStore_client = MagicMock() + oci_client.return_value = objectStore_client + namespace_name = "namespace_name" + bucket_name = "bucket_name" + delete_bucket(namespace_name, bucket_name) + objectStore_client.delete_bucket.assert_called_with(namespace_name, bucket_name) + + +@patch('chaosoci.core.objectStorage.actions.filter_buckets', autospec=True) +@patch('chaosoci.core.objectStorage.actions.get_buckets', autospec=True) +@patch('chaosoci.core.objectStorage.actions.oci_client', autospec=True) +def test_delete_buckets_in_compartment(oci_client, get_buckets, filter_buckets): + objectStore_client = MagicMock() + bucket_mock = MagicMock() + oci_client.return_value = objectStore_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + namespace_name = "namespace_name" + + bucket_mock = [bucket_mock, bucket_mock, bucket_mock, bucket_mock] + filter_buckets.return_value = bucket_mock + + delete_buckets_in_compartment(filters=filters, namespace_name=namespace_name, bucket_names=[], + compartment_id=c_id) + + filter_buckets.assert_called_with(buckets=get_buckets(oci_client, + c_id), + filters=filters) + + +@patch('chaosoci.core.objectStorage.actions.oci_client', autospec=True) +def test_delete_object(oci_client): + objectStore_client = MagicMock() + oci_client.return_value = objectStore_client + namespace_name = "namespace_name" + bucket_name = "bucket_name" + object_name = "object_name" + delete_object(namespace_name, bucket_name, object_name) + objectStore_client.delete_object.assert_called_with(namespace_name, bucket_name, object_name) + + +@patch('chaosoci.core.objectStorage.actions.filter_obstore_objects', autospec=True) +@patch('chaosoci.core.objectStorage.actions.get_objects', autospec=True) +@patch('chaosoci.core.objectStorage.actions.oci_client', autospec=True) +def test_delete_objects_in_compartment(oci_client, get_objects, filter_obstore_objects): + objectStore_client = MagicMock() + object_mock = MagicMock() + oci_client.return_value = objectStore_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + namespace_name = "namespace_name" + bucket_name = "bucket_name" + + object_mock = [object_mock, object_mock, object_mock, object_mock] + filter_obstore_objects.return_value = object_mock + + delete_objects_in_compartment(filters=filters, namespace_name=namespace_name, bucket_name=bucket_name, + object_names=[], compartment_id=c_id) + + filter_obstore_objects.assert_called_with(objects=get_objects(oci_client, + c_id), + filters=filters) + diff --git a/tests/core/ObjectStorage/test_objectStorage_probes.py b/tests/core/ObjectStorage/test_objectStorage_probes.py new file mode 100644 index 0000000..fc5db57 --- /dev/null +++ b/tests/core/ObjectStorage/test_objectStorage_probes.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from unittest import TestCase as T +from unittest.mock import MagicMock, patch + +from chaosoci.core.objectStorage.probes import count_buckets, count_objects + + +@patch('chaosoci.core.objectStorage.probes.filter_buckets', autospec=True) +@patch('chaosoci.core.objectStorage.probes.get_buckets', autospec=True) +@patch('chaosoci.core.objectStorage.probes.oci_client', autospec=True) +def test_count_buckets(oci_client, get_buckets, filter_buckets): + compute_client = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + count_buckets(filters=filters, compartment_id=c_id) + filter_buckets.assert_called_with(get_buckets(oci_client, c_id), + filters) + + +@patch('chaosoci.core.objectStorage.probes.filter_obstore_objects', autospec=True) +@patch('chaosoci.core.objectStorage.probes.get_objects', autospec=True) +@patch('chaosoci.core.objectStorage.probes.oci_client', autospec=True) +def test_count_objects(oci_client, get_objects, filter_obstore_objects): + compute_client = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + count_objects(filters=filters, compartment_id=c_id) + filter_obstore_objects.assert_called_with(get_objects(oci_client, c_id), + filters) diff --git a/tests/core/compute/test_compute_actions.py b/tests/core/compute/test_compute_actions.py index 0c2ba34..024ab70 100644 --- a/tests/core/compute/test_compute_actions.py +++ b/tests/core/compute/test_compute_actions.py @@ -1,7 +1,13 @@ # -*- coding: utf-8 -*- from unittest.mock import MagicMock, patch -from chaosoci.core.compute.actions import stop_instance, stop_random_instance +from chaosoci.core.compute.actions import stop_instance, stop_random_instance, stop_instances_in_compartment, \ + start_instance_pool, start_all_instance_pools_in_compartment, \ + stop_instance_pool, stop_all_instance_pools_in_compartment, \ + terminate_instance_pool, terminate_all_instance_pools_in_compartment, \ + reset_instance_pool, reset_all_instance_pools_in_compartment, \ + softreset_instance_pool, softreset_all_instance_pools_in_compartment + @patch('chaosoci.core.compute.actions.oci_client', autospec=True) def test_stop_instance(oci_client): @@ -54,3 +60,202 @@ def test_stop_random_instances(oci_client, get_instances, filter_instances): compute_client.instance_action.assert_called_with(instance_id=instance.id, action=action) + + +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_start_instance_pool(oci_client): + compute_client = MagicMock() + oci_client.return_value = compute_client + instance_pool_id = "i-1234567890abcdef0" + start_instance_pool(instance_pool_id) + compute_client.start_instance_pool.assert_called_with(instance_pool_id) + + +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_stop_instance_pool(oci_client): + compute_client = MagicMock() + oci_client.return_value = compute_client + inst_id = "i-1234567890abcdef0" + stop_instance_pool(inst_id) + compute_client.stop_instance_pool.assert_called_with(inst_id) + + +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_terminate_instance_pool(oci_client): + compute_client = MagicMock() + oci_client.return_value = compute_client + inst_id = "i-1234567890abcdef0" + terminate_instance_pool(inst_id) + compute_client.terminate_instance_pool.assert_called_with(inst_id) + + +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_reset_instance_pool(oci_client): + compute_client = MagicMock() + oci_client.return_value = compute_client + inst_id = "i-1234567890abcdef0" + reset_instance_pool(inst_id) + compute_client.reset_instance_pool.assert_called_with(inst_id) + + +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_softreset_instance_pool(oci_client): + compute_client = MagicMock() + oci_client.return_value = compute_client + inst_id = "i-1234567890abcdef0" + softreset_instance_pool(inst_id) + compute_client.softreset_instance_pool.assert_called_with(inst_id) + + +@patch('chaosoci.core.compute.actions.filter_instances', autospec=True) +@patch('chaosoci.core.compute.actions.get_instances', autospec=True) +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_stop_instances_in_compartment(oci_client, get_instances, filter_instances): + compute_client = MagicMock() + instance = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + instance.id = "i-1234567890abcdef0" + instances = [instance, instance, instance, instance] + filter_instances.return_value = instances + + action = "SOFTSTOP" + + stop_random_instance(filters=filters, compartment_id=c_id) + + compute_client.instance_action.assert_called_with(instance_id=instance.id, action=action) + + +@patch('chaosoci.core.compute.actions.filter_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.get_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_start_all_instance_pools_in_compartment(oci_client, get_instance_pools, filter_instance_pools): + compute_client = MagicMock() + instance = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + instance.id = "i-1234567890abcdef0" + instances = [instance, instance, instance, instance] + filter_instance_pools.return_value = instances + + start_all_instance_pools_in_compartment(instance_pool_ids=[], filters=filters, compartment_id=c_id) + + filter_instance_pools.assert_called_with(get_instance_pools(oci_client, + c_id), + filters) + + +@patch('chaosoci.core.compute.actions.filter_instances', autospec=True) +@patch('chaosoci.core.compute.actions.get_instances', autospec=True) +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_stop_instances_in_compartment(oci_client, get_instances, filter_instances): + compute_client = MagicMock() + instance = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + instance.id = "i-1234567890abcdef0" + instances = [instance, instance, instance, instance] + filter_instances.return_value = instances + + stop_instances_in_compartment(instances_ids=[], filters=filters, compartment_id=c_id) + + filter_instances.assert_called_with(instances=get_instances(oci_client, + c_id), + filters=filters) + + +@patch('chaosoci.core.compute.actions.filter_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.get_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_stop_all_instance_pools_in_compartment(oci_client, get_instance_pools, filter_instance_pools): + compute_client = MagicMock() + instance = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + instance.id = "i-1234567890abcdef0" + instances = [instance, instance, instance, instance] + filter_instance_pools.return_value = instances + + stop_all_instance_pools_in_compartment(instance_pool_ids=[], filters=filters, compartment_id=c_id) + + filter_instance_pools.assert_called_with(get_instance_pools(oci_client, + c_id), + filters) + + +@patch('chaosoci.core.compute.actions.filter_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.get_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_terminate_all_instance_pools_in_compartment(oci_client, get_instance_pools, filter_instance_pools): + compute_client = MagicMock() + instance = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + instance.id = "i-1234567890abcdef0" + instances = [instance, instance, instance, instance] + filter_instance_pools.return_value = instances + + terminate_all_instance_pools_in_compartment(instance_pool_ids=[], filters=filters, compartment_id=c_id) + + filter_instance_pools.assert_called_with(get_instance_pools(oci_client, + c_id), + filters) + + +@patch('chaosoci.core.compute.actions.filter_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.get_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_reset_all_instance_pools_in_compartment(oci_client, get_instance_pools, filter_instance_pools): + compute_client = MagicMock() + instance = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + instance.id = "i-1234567890abcdef0" + instances = [instance, instance, instance, instance] + filter_instance_pools.return_value = instances + + reset_all_instance_pools_in_compartment(instance_pool_ids=[], filters=filters, compartment_id=c_id) + + filter_instance_pools.assert_called_with(get_instance_pools(oci_client, + c_id), + filters) + + +@patch('chaosoci.core.compute.actions.filter_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.get_instance_pools', autospec=True) +@patch('chaosoci.core.compute.actions.oci_client', autospec=True) +def test_softreset_all_instance_pools_in_compartment(oci_client, get_instance_pools, filter_instance_pools): + compute_client = MagicMock() + instance = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + instance.id = "i-1234567890abcdef0" + instances = [instance, instance, instance, instance] + filter_instance_pools.return_value = instances + + softreset_all_instance_pools_in_compartment(instance_pool_ids=[], filters=filters, compartment_id=c_id) + + filter_instance_pools.assert_called_with(get_instance_pools(oci_client, + c_id), + filters) diff --git a/tests/core/compute/test_compute_probes.py b/tests/core/compute/test_compute_probes.py index b5a2701..2eb1d17 100644 --- a/tests/core/compute/test_compute_probes.py +++ b/tests/core/compute/test_compute_probes.py @@ -2,7 +2,7 @@ from unittest import TestCase as T from unittest.mock import MagicMock, patch -from chaosoci.core.compute.probes import count_instances +from chaosoci.core.compute.probes import count_instances, count_instance_pools @patch('chaosoci.core.compute.probes.filter_instances', autospec=True) @@ -34,3 +34,34 @@ def test_count_instances_ret_int(oci_client, get_instances, filter_instances): n = count_instances(filters=filters, compartment_id=c_id) T().assertEqual(n, 3) + + +@patch('chaosoci.core.compute.probes.filter_instances', autospec=True) +@patch('chaosoci.core.compute.probes.get_instance_pools', autospec=True) +@patch('chaosoci.core.compute.probes.oci_client', autospec=True) +def test_count_instances(oci_client, get_instance_pools, filter_instances): + compute_client = MagicMock() + oci_client.return_value = compute_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + count_instance_pools(filters=filters, compartment_id=c_id) + filter_instances.assert_called_with(instances=get_instance_pools(oci_client, + c_id), + filters=filters) + + +@patch('chaosoci.core.compute.probes.filter_instances', autospec=True) +@patch('chaosoci.core.compute.probes.get_instance_pools', autospec=True) +@patch('chaosoci.core.compute.probes.oci_client', autospec=True) +def test_count_instances_ret_int(oci_client, get_instance_pools, filter_instances): + compute_client = MagicMock() + oci_client.return_value = compute_client + + filter_instances.return_value = ['one', 'two', 'three'] + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + n = count_instance_pools(filters=filters, compartment_id=c_id) + T().assertEqual(n, 3) diff --git a/tests/core/networking/test_networking_actions.py b/tests/core/networking/test_networking_actions.py index 11f974e..d32d3ee 100644 --- a/tests/core/networking/test_networking_actions.py +++ b/tests/core/networking/test_networking_actions.py @@ -7,8 +7,11 @@ from chaoslib.exceptions import ActivityFailed -from chaosoci.core.networking.actions import (delete_route_table_by_id, - delete_route_table_by_filters) +from chaosoci.core.networking.actions import (delete_route_table_by_id, delete_route_table_by_filters, + delete_nat_gateway_by_id, delete_nat_gateway_by_filters, + delete_internet_gateway_by_id, delete_internet_gateway_by_filters, + delete_service_gateway_by_id, delete_service_gateway_by_filters) +from chaosoci.core.networking.common import get_nat_gateway from chaosoci.util.constants import FILTER_ERR # FILTER_ERR = 'Some of the chosen filters were not found, we cannot continue.' @@ -60,3 +63,150 @@ def test_delete_route_table_by_filters(oci_client, get_route_tables, network_client.delete_route_table.assert_called_with( filter_route_tables(route_tables=get_route_tables( oci_client, c, v), filters=f)[0].id) + +@patch('chaosoci.core.networking.actions.oci_client', autospec=True) +def test_delete_nat_gateway_by_id(oci_client): + network_client = MagicMock() + oci_client.return_value = network_client + nw_id = "ocid1.routetable.oc1.phx.aawnm2cdxq3naniep5dsiixtchqjuypcx7l7" + nw_ids = [nw_id, ""] + for id in nw_ids: + if id == nw_id: + delete_nat_gateway_by_id(id) + network_client.delete_nat_gateway.assert_called_with(nw_id=id) + else: + with pytest.raises(ActivityFailed) as f: + delete_route_table_by_id(id) + assert 'A route table id is required.' + +@patch('chaosoci.core.networking.actions.filter_nat_gateway', autospec=True) +@patch('chaosoci.core.networking.actions.get_nat_gateway', autospec=True) +@patch('chaosoci.core.networking.actions.oci_client', autospec=True) +def test_delete_nat_gateway_by_filters(oci_client, get_nat_gateway, + filter_nat_gateway): + network_client = MagicMock() + oci_client.return_value = network_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + vcn_id = "ocid1.vcn.oc1.phx.amaaaaaapwxjxiqavc6zohqv4whr6y65qwwjcexhex" + + c_ids = [c_id, None] + vcn_ids = [vcn_id, None] + filters = [[{'display_name': 'random_name', 'region': 'uk-london-1'}], + None] + + for c in c_ids: + for v in vcn_ids: + for f in filters: + if c is None or v is None: + with pytest.raises(ActivityFailed) as c_failed: + delete_nat_gateway_by_filters(c, v, f) + assert 'A compartment id or vcn id is required.' + elif f is None: + with pytest.raises(ActivityFailed) as f_failed: + delete_nat_gateway_by_filters(c, v, f) + assert FILTER_ERR + else: + with pytest.raises(ActivityFailed) as rt_failed: + delete_nat_gateway_by_filters(c, v, f) + network_client.delete_nat_gateway.assert_called_with( + filter_nat_gateway(route_tables=get_nat_gateway( + oci_client, c, v), filters=f)[0].id) + +@patch('chaosoci.core.networking.actions.oci_client', autospec=True) +def test_delete_internet_gateway_by_id(oci_client): + network_client = MagicMock() + oci_client.return_value = network_client + ig_id = "ocid1.routetable.oc1.phx.aawnm2cdxq3naniep5dsiixtchqjuypcx7l7" + ig_ids = [ig_id, ""] + for id in ig_ids: + if id == ig_id: + delete_internet_gateway_by_id(id) + network_client.delete_internet_gateway.assert_called_with(ig_id=id) + else: + with pytest.raises(ActivityFailed) as f: + delete_internet_gateway_by_id(id) + assert 'A route table id is required.' + +@patch('chaosoci.core.networking.actions.filter_internet_gateway', autospec=True) +@patch('chaosoci.core.networking.actions.get_internet_gateway', autospec=True) +@patch('chaosoci.core.networking.actions.oci_client', autospec=True) +def test_delete_internet_gateway_by_filters(oci_client, get_route_tables, + filter_internet_gateway): + network_client = MagicMock() + oci_client.return_value = network_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + vcn_id = "ocid1.vcn.oc1.phx.amaaaaaapwxjxiqavc6zohqv4whr6y65qwwjcexhex" + + c_ids = [c_id, None] + vcn_ids = [vcn_id, None] + filters = [[{'display_name': 'random_name', 'region': 'uk-london-1'}], + None] + + for c in c_ids: + for v in vcn_ids: + for f in filters: + if c is None or v is None: + with pytest.raises(ActivityFailed) as c_failed: + delete_internet_gateway_by_filters(c, v, f) + assert 'A compartment id or vcn id is required.' + elif f is None: + with pytest.raises(ActivityFailed) as f_failed: + delete_internet_gateway_by_filters(c, v, f) + assert FILTER_ERR + else: + with pytest.raises(ActivityFailed) as rt_failed: + delete_internet_gateway_by_filters(c, v, f) + network_client.delete_internet_gateway.assert_called_with( + filter_internet_gateway(route_tables=get_route_tables( + oci_client, c, v), filters=f)[0].id) + +@patch('chaosoci.core.networking.actions.oci_client', autospec=True) +def test_delete_service_gateway_by_id(oci_client): + network_client = MagicMock() + oci_client.return_value = network_client + sg_id = "ocid1.routetable.oc1.phx.aawnm2cdxq3naniep5dsiixtchqjuypcx7l7" + sg_ids = [sg_id, ""] + for id in sg_ids: + if id == sg_id: + delete_service_gateway_by_id(id) + network_client.delete_service_gateway.assert_called_with(sg_id=id) + else: + with pytest.raises(ActivityFailed) as f: + delete_service_gateway_by_id(id) + assert 'A route table id is required.' + +@patch('chaosoci.core.networking.actions.filter_service_gateway', autospec=True) +@patch('chaosoci.core.networking.actions.get_service_gateway', autospec=True) +@patch('chaosoci.core.networking.actions.oci_client', autospec=True) +def test_delete_service_gateway_by_filters(oci_client, get_service_gateway, + filter_service_gateway): + network_client = MagicMock() + oci_client.return_value = network_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + vcn_id = "ocid1.vcn.oc1.phx.amaaaaaapwxjxiqavc6zohqv4whr6y65qwwjcexhex" + + c_ids = [c_id, None] + vcn_ids = [vcn_id, None] + filters = [[{'display_name': 'random_name', 'region': 'uk-london-1'}], + None] + + for c in c_ids: + for v in vcn_ids: + for f in filters: + if c is None or v is None: + with pytest.raises(ActivityFailed) as c_failed: + delete_service_gateway_by_filters(c, v, f) + assert 'A compartment id or vcn id is required.' + elif f is None: + with pytest.raises(ActivityFailed) as f_failed: + delete_service_gateway_by_filters(c, v, f) + assert FILTER_ERR + else: + with pytest.raises(ActivityFailed) as rt_failed: + delete_service_gateway_by_filters(c, v, f) + network_client.delete_service_gateway.assert_called_with( + filter_service_gateway(get_service_gateway( + oci_client, c, v), filters=f)[0].id) diff --git a/tests/core/networking/test_networking_probes.py b/tests/core/networking/test_networking_probes.py index 5d1c614..fd43041 100644 --- a/tests/core/networking/test_networking_probes.py +++ b/tests/core/networking/test_networking_probes.py @@ -9,7 +9,8 @@ from chaoslib.exceptions import ActivityFailed from chaosoci.core.networking.probes import (count_route_tables, - filter_route_tables) + filter_route_tables, count_nat_gateway, count_internet_gateway, + count_service_gateway) @patch('chaosoci.core.networking.probes.filter_route_tables', autospec=True) @patch('chaosoci.core.networking.probes.get_route_tables', autospec=True) @@ -27,9 +28,78 @@ def test_count_route_tables(oci_client, get_route_tables, filter_route_tables): if id == c_id: count_route_tables(filters=filters, compartment_id=id) filter_route_tables.assert_called_with( - route_tables=get_route_tables( - oci_client, id), filters=filters) + get_route_tables( + oci_client, id), filters) else: with pytest.raises(ActivityFailed) as f: count_route_tables(filters=filters, compartment_id=id) assert 'A valid compartment id is required.' + +@patch('chaosoci.core.networking.probes.filter_nat_gateway', autospec=True) +@patch('chaosoci.core.networking.probes.get_nat_gateway', autospec=True) +@patch('chaosoci.core.networking.probes.oci_client', autospec=True) +def test_count_nat_gateway(oci_client, get_nat_gateway, filter_nat_gateway): + network_client = MagicMock() + oci_client.return_value = network_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + c_ids = [c_id] + + for id in c_ids: + if id == c_id: + count_nat_gateway(filters=filters, compartment_id=id) + filter_nat_gateway.assert_called_with( + get_nat_gateway( + oci_client, id), filters) + else: + with pytest.raises(ActivityFailed) as f: + count_nat_gateway(filters=filters, compartment_id=id) + assert 'A valid compartment id is required.' + +@patch('chaosoci.core.networking.probes.filter_internet_gateway', autospec=True) +@patch('chaosoci.core.networking.probes.get_internet_gateway', autospec=True) +@patch('chaosoci.core.networking.probes.oci_client', autospec=True) +def test_count_internet_gateway(oci_client, get_internet_gateway, filter_internet_gateway): + network_client = MagicMock() + oci_client.return_value = network_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + c_ids = [c_id] + + for id in c_ids: + if id == c_id: + count_internet_gateway(filters=filters, compartment_id=id) + filter_internet_gateway.assert_called_with( + get_internet_gateway( + oci_client, id), filters) + else: + with pytest.raises(ActivityFailed) as f: + count_internet_gateway(filters=filters, compartment_id=id) + assert 'A valid compartment id is required.' + +@patch('chaosoci.core.networking.probes.filter_service_gateway', autospec=True) +@patch('chaosoci.core.networking.probes.get_service_gateway', autospec=True) +@patch('chaosoci.core.networking.probes.oci_client', autospec=True) +def test_count_service_gateway(oci_client, get_service_gateway, filter_service_gateway): + network_client = MagicMock() + oci_client.return_value = network_client + + c_id = "ocid1.compartment.oc1..oadsocmof6r6ksovxmda44ikwxje7xxu" + filters = [{'display_name': 'random_name', 'region': 'uk-london-1'}] + + c_ids = [c_id] + + for id in c_ids: + if id == c_id: + count_service_gateway(filters=filters, compartment_id=id) + filter_service_gateway.assert_called_with( + get_service_gateway( + oci_client, id), filters) + else: + with pytest.raises(ActivityFailed) as f: + count_service_gateway(filters=filters, compartment_id=id) + assert 'A valid compartment id is required.' \ No newline at end of file