ADMIN_NAMESPACE = "kube-system"
RBAC_STACK_PREFIX = "juju-credential"
-# from juju.bundle import BundleHandler
-# import re
-# import ssl
-# from .vnf import N2VC
-
def generate_rbac_id():
return binascii.hexlify(os.urandom(4)).decode()
(on error, an exception will be raised)
"""
- # """Bootstrapping
-
- # Bootstrapping cannot be done, by design, through the API. We need to
- # use the CLI tools.
- # """
-
- # """
- # WIP: Workflow
-
- # 1. Has the environment already been bootstrapped?
- # - Check the database to see if we have a record for this env
-
- # 2. If this is a new env, create it
- # - Add the k8s cloud to Juju
- # - Bootstrap
- # - Record it in the database
-
- # 3. Connect to the Juju controller for this cloud
-
- # """
- # cluster_uuid = reuse_cluster_uuid
- # if not cluster_uuid:
- # cluster_uuid = str(uuid4())
-
- ##################################################
- # TODO: Pull info from db based on the namespace #
- ##################################################
-
- ###################################################
- # TODO: Make it idempotent, calling add-k8s and #
- # bootstrap whenever reuse_cluster_uuid is passed #
- # as parameter #
- # `init_env` is called to initialize the K8s #
- # cluster for juju. If this initialization fails, #
- # it can be called again by LCM with the param #
- # reuse_cluster_uuid, e.g. to try to fix it. #
- ###################################################
-
- # This is a new cluster, so bootstrap it
-
cluster_uuid = reuse_cluster_uuid or str(uuid.uuid4())
- # Is a local k8s cluster?
- # localk8s = self.is_local_k8s(k8s_creds)
-
- # If the k8s is external, the juju controller needs a loadbalancer
- # loadbalancer = False if localk8s else True
-
- # Name the new k8s cloud
- # k8s_cloud = "k8s-{}".format(cluster_uuid)
-
- # self.log.debug("Adding k8s cloud {}".format(k8s_cloud))
- # await self.add_k8s(k8s_cloud, k8s_creds)
-
- # Bootstrap Juju controller
- # self.log.debug("Bootstrapping...")
- # await self.bootstrap(k8s_cloud, cluster_uuid, loadbalancer)
- # self.log.debug("Bootstrap done.")
-
- # Get the controller information
-
- # Parse ~/.local/share/juju/controllers.yaml
- # controllers.testing.api-endpoints|ca-cert|uuid
- # self.log.debug("Getting controller endpoints")
- # with open(os.path.expanduser("~/.local/share/juju/controllers.yaml")) as f:
- # controllers = yaml.load(f, Loader=yaml.Loader)
- # controller = controllers["controllers"][cluster_uuid]
- # endpoints = controller["api-endpoints"]
- # juju_endpoint = endpoints[0]
- # juju_ca_cert = controller["ca-cert"]
-
- # Parse ~/.local/share/juju/accounts
- # controllers.testing.user|password
- # self.log.debug("Getting accounts")
- # with open(os.path.expanduser("~/.local/share/juju/accounts.yaml")) as f:
- # controllers = yaml.load(f, Loader=yaml.Loader)
- # controller = controllers["controllers"][cluster_uuid]
-
- # juju_user = controller["user"]
- # juju_secret = controller["password"]
-
- # config = {
- # "endpoint": juju_endpoint,
- # "username": juju_user,
- # "secret": juju_secret,
- # "cacert": juju_ca_cert,
- # "loadbalancer": loadbalancer,
- # }
-
- # Store the cluster configuration so it
- # can be used for subsequent calls
kubecfg = tempfile.NamedTemporaryFile()
with open(kubecfg.name, "w") as kubecfg_file:
kubecfg_file.write(k8s_creds)
storage_class=default_storage_class,
credential_name=self._get_credential_name(cluster_uuid),
)
- # self.log.debug("Setting config")
- # await self.set_config(cluster_uuid, config)
-
- # Test connection
- # controller = await self.get_controller(cluster_uuid)
- # await controller.disconnect()
-
- # TODO: Remove these commented lines
- # raise Exception("EOL")
- # self.juju_public_key = None
- # Login to the k8s cluster
- # if not self.authenticated:
- # await self.login(cluster_uuid)
-
- # We're creating a new cluster
- # print("Getting model {}".format(self.get_namespace(cluster_uuid),
- # cluster_uuid=cluster_uuid))
- # model = await self.get_model(
- # self.get_namespace(cluster_uuid),
- # cluster_uuid=cluster_uuid
- # )
-
- # Disconnect from the model
- # if model and model.is_connected():
- # await model.disconnect()
-
return cluster_uuid, True
except Exception as e:
self.log.error("Error initializing k8scluster: {}".format(e))
"""
try:
- # Remove k8scluster from database
- # self.log.debug("[reset] Removing k8scluster from juju database")
- # juju_db = self.db.get_one("admin", {"_id": "juju"})
-
- # for k in juju_db["k8sclusters"]:
- # if k["_id"] == cluster_uuid:
- # juju_db["k8sclusters"].remove(k)
- # self.db.set_one(
- # table="admin",
- # q_filter={"_id": "juju"},
- # update_dict={"k8sclusters": juju_db["k8sclusters"]},
- # )
- # break
-
- # Destroy the controller (via CLI)
- # self.log.debug("[reset] Destroying controller")
- # await self.destroy_controller(cluster_uuid)
self.log.debug("[reset] Removing k8s cloud")
- # k8s_cloud = "k8s-{}".format(cluster_uuid)
- # await self.remove_cloud(k8s_cloud)
cloud_creds = await self.libjuju.get_cloud_credentials(
cluster_uuid,
self.log.debug("Caught exception during reset: {}".format(e))
raise e
return True
- # TODO: Remove these commented lines
- # if not self.authenticated:
- # await self.login(cluster_uuid)
-
- # if self.controller.is_connected():
- # # Destroy the model
- # namespace = self.get_namespace(cluster_uuid)
- # if await self.has_model(namespace):
- # self.log.debug("[reset] Destroying model")
- # await self.controller.destroy_model(namespace, destroy_storage=True)
-
- # # Disconnect from the controller
- # self.log.debug("[reset] Disconnecting controller")
- # await self.logout()
"""Deployment"""
"""
bundle = kdu_model
- # controller = await self.get_controller(cluster_uuid)
-
- ##
- # Get or create the model, based on the NS
- # uuid.
-
if not db_dict:
raise K8sException("db_dict must be set")
if not bundle:
await self.libjuju.deploy(
bundle, model_name=kdu_instance, wait=atomic, timeout=timeout
)
-
- # Get the application
- # if atomic:
- # # applications = model.applications
- # self.log.debug("[install] Applications: {}".format(model.applications))
- # for name in model.applications:
- # self.log.debug("[install] Waiting for {} to settle".format(name))
- # application = model.applications[name]
- # try:
- # # It's not enough to wait for all units to be active;
- # # the application status needs to be active as well.
- # self.log.debug("Waiting for all units to be active...")
- # await model.block_until(
- # lambda: all(
- # unit.agent_status == "idle"
- # and application.status in ["active", "unknown"]
- # and unit.workload_status in ["active", "unknown"]
- # for unit in application.units
- # ),
- # timeout=timeout,
- # )
- # self.log.debug("All units active.")
-
- # # TODO use asyncio.TimeoutError
- # except concurrent.futures._base.TimeoutError:
- # os.chdir(previous_workdir)
- # self.log.debug("[install] Timeout exceeded; resetting cluster")
- # await self.reset(cluster_uuid)
- # return False
-
- # Wait for the application to be active
- # if model.is_connected():
- # self.log.debug("[install] Disconnecting model")
- # await model.disconnect()
- # await controller.disconnect()
os.chdir(previous_workdir)
-
return kdu_instance
async def instances_list(self, cluster_uuid: str) -> list:
initial release.
"""
raise MethodNotImplemented()
- # TODO: Remove these commented lines
-
- # model = await self.get_model(namespace, cluster_uuid=cluster_uuid)
-
- # model = None
- # namespace = self.get_namespace(cluster_uuid)
- # controller = await self.get_controller(cluster_uuid)
-
- # try:
- # if namespace not in await controller.list_models():
- # raise N2VCNotFound(message="Model {} does not exist".format(namespace))
-
- # model = await controller.get_model(namespace)
- # with open(kdu_model, "r") as f:
- # bundle = yaml.safe_load(f)
-
- # """
- # {
- # 'description': 'Test bundle',
- # 'bundle': 'kubernetes',
- # 'applications': {
- # 'mariadb-k8s': {
- # 'charm': 'cs:~charmed-osm/mariadb-k8s-20',
- # 'scale': 1,
- # 'options': {
- # 'password': 'manopw',
- # 'root_password': 'osm4u',
- # 'user': 'mano'
- # },
- # 'series': 'kubernetes'
- # }
- # }
- # }
- # """
- # # TODO: This should be returned in an agreed-upon format
- # for name in bundle["applications"]:
- # self.log.debug(model.applications)
- # application = model.applications[name]
- # self.log.debug(application)
-
- # path = bundle["applications"][name]["charm"]
-
- # try:
- # await application.upgrade_charm(switch=path)
- # except juju.errors.JujuError as ex:
- # if "already running charm" in str(ex):
- # # We're already running this version
- # pass
- # finally:
- # if model:
- # await model.disconnect()
- # await controller.disconnect()
- # return True
"""Rollback"""
:return: Returns True if successful, or raises an exception
"""
- # controller = await self.get_controller(cluster_uuid)
-
self.log.debug("[uninstall] Destroying model")
await self.libjuju.destroy_model(kdu_instance, total_timeout=3600)
:return: Returns the output of the action
"""
- # controller = await self.get_controller(cluster_uuid)
-
if not params or "application-name" not in params:
raise K8sException(
"Missing application-name argument, \
output, status = await self.libjuju.execute_action(
application_name, kdu_instance, primitive_name, **params
)
- # model = await self.get_model(kdu_instance, controller=controller)
-
- # application_name = params["application-name"]
- # application = model.applications[application_name]
-
- # actions = await application.get_actions()
- # if primitive_name not in actions:
- # raise K8sException("Primitive {} not found".format(primitive_name))
-
- # unit = None
- # for u in application.units:
- # if await u.is_leader_from_status():
- # unit = u
- # break
-
- # if unit is None:
- # raise K8sException("No leader unit found to execute action")
-
- # self.log.debug("[exec_primitive] Running action: {}".format(primitive_name))
- # action = await unit.run_action(primitive_name, **params)
-
- # output = await model.get_action_output(action_uuid=action.entity_id)
- # status = await model.get_action_status(uuid_or_prefix=action.entity_id)
-
- # status = (
- # status[action.entity_id] if action.entity_id in status else "failed"
- # )
if status != "completed":
raise K8sException(
error_msg = "Error executing primitive {}: {}".format(primitive_name, e)
self.log.error(error_msg)
raise K8sException(message=error_msg)
- # finally:
- # await controller.disconnect()
- # TODO: Remove these commented lines:
- # if not self.authenticated:
- # self.log.debug("[exec_primitive] Connecting to controller")
- # await self.login(cluster_uuid)
"""Introspection"""
and deployment_time.
"""
status = {}
- # controller = await self.get_controller(cluster_uuid)
- # model = await self.get_model(kdu_instance, controller=controller)
-
- # model_status = await model.get_status()
- # status = model_status.applications
model_status = await self.libjuju.get_model_status(kdu_instance)
for name in model_status.applications:
application = model_status.applications[name]
status[name] = {"status": application["status"]["status"]}
- # await model.disconnect()
- # await controller.disconnect()
-
return status
async def get_services(
credentials = self.get_credentials(cluster_uuid=cluster_uuid)
- # config_path = "/tmp/{}".format(cluster_uuid)
- # config_file = "{}/config".format(config_path)
-
- # if not os.path.exists(config_path):
- # os.makedirs(config_path)
- # with open(config_file, "w") as f:
- # f.write(credentials)
-
kubecfg = tempfile.NamedTemporaryFile()
with open(kubecfg.name, "w") as kubecfg_file:
kubecfg_file.write(credentials)
credentials = self.get_credentials(cluster_uuid=cluster_uuid)
- # config_path = "/tmp/{}".format(cluster_uuid)
- # config_file = "{}/config".format(config_path)
-
- # if not os.path.exists(config_path):
- # os.makedirs(config_path)
- # with open(config_file, "w") as f:
- # f.write(credentials)
-
kubecfg = tempfile.NamedTemporaryFile()
with open(kubecfg.name, "w") as kubecfg_file:
kubecfg_file.write(credentials)
)
)[0]
- # Private methods
- # async def add_k8s(self, cloud_name: str, credentials: str,) -> bool:
- # """Add a k8s cloud to Juju
-
- # Adds a Kubernetes cloud to Juju, so it can be bootstrapped with a
- # Juju Controller.
-
- # :param cloud_name str: The name of the cloud to add.
- # :param credentials dict: A dictionary representing the output of
- # `kubectl config view --raw`.
-
- # :returns: True if successful, otherwise raises an exception.
- # """
-
- # cmd = [self.juju_command, "add-k8s", "--local", cloud_name]
- # self.log.debug(cmd)
-
- # process = await asyncio.create_subprocess_exec(
- # *cmd,
- # stdout=asyncio.subprocess.PIPE,
- # stderr=asyncio.subprocess.PIPE,
- # stdin=asyncio.subprocess.PIPE,
- # )
-
- # # Feed the process the credentials
- # process.stdin.write(credentials.encode("utf-8"))
- # await process.stdin.drain()
- # process.stdin.close()
-
- # _stdout, stderr = await process.communicate()
-
- # return_code = process.returncode
-
- # self.log.debug("add-k8s return code: {}".format(return_code))
-
- # if return_code > 0:
- # raise Exception(stderr)
-
- # return True
-
- # async def add_model(
- # self, model_name: str, cluster_uuid: str, controller: Controller
- # ) -> Model:
- # """Adds a model to the controller
-
- # Adds a new model to the Juju controller
-
- # :param model_name str: The name of the model to add.
- # :param cluster_uuid str: ID of the cluster.
- # :param controller: Controller object in which the model will be added
- # :returns: The juju.model.Model object of the new model upon success or
- # raises an exception.
- # """
-
- # self.log.debug(
- # "Adding model '{}' to cluster_uuid '{}'".format(model_name, cluster_uuid)
- # )
- # model = None
- # try:
- # if self.juju_public_key is not None:
- # model = await controller.add_model(
- # model_name, config={"authorized-keys": self.juju_public_key}
- # )
- # else:
- # model = await controller.add_model(model_name)
- # except Exception as ex:
- # self.log.debug(ex)
- # self.log.debug("Caught exception: {}".format(ex))
- # pass
-
- # return model
-
- # async def bootstrap(
- # self, cloud_name: str, cluster_uuid: str, loadbalancer: bool
- # ) -> bool:
- # """Bootstrap a Kubernetes controller
-
- # Bootstrap a Juju controller inside the Kubernetes cluster
-
- # :param cloud_name str: The name of the cloud.
- # :param cluster_uuid str: The UUID of the cluster to bootstrap.
- # :param loadbalancer bool: If the controller should use loadbalancer or not.
- # :returns: True upon success or raises an exception.
- # """
-
- # if not loadbalancer:
- # cmd = [self.juju_command, "bootstrap", cloud_name, cluster_uuid]
- # else:
- # """
- # For public clusters, specify that the controller service is using a
- # LoadBalancer.
- # """
- # cmd = [
- # self.juju_command,
- # "bootstrap",
- # cloud_name,
- # cluster_uuid,
- # "--config",
- # "controller-service-type=loadbalancer",
- # ]
-
- # self.log.debug(
- # "Bootstrapping controller {} in cloud {}".format(cluster_uuid, cloud_name)
- # )
-
- # process = await asyncio.create_subprocess_exec(
- # *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
- # )
-
- # _stdout, stderr = await process.communicate()
-
- # return_code = process.returncode
-
- # if return_code > 0:
- # #
- # if b"already exists" not in stderr:
- # raise Exception(stderr)
-
- # return True
-
- # async def destroy_controller(self, cluster_uuid: str) -> bool:
- # """Destroy a Kubernetes controller
-
- # Destroy an existing Kubernetes controller.
-
- # :param cluster_uuid str: The UUID of the cluster to bootstrap.
- # :returns: True upon success or raises an exception.
- # """
- # cmd = [
- # self.juju_command,
- # "destroy-controller",
- # "--destroy-all-models",
- # "--destroy-storage",
- # "-y",
- # cluster_uuid,
- # ]
-
- # process = await asyncio.create_subprocess_exec(
- # *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
- # )
-
- # _stdout, stderr = await process.communicate()
-
- # return_code = process.returncode
-
- # if return_code > 0:
- # #
- # if "already exists" not in stderr:
- # raise Exception(stderr)
-
def get_credentials(self, cluster_uuid: str) -> str:
"""
Get Cluster Kubeconfig
"""
return "cred-{}".format(cluster_uuid)
- # def get_config(self, cluster_uuid: str,) -> dict:
- # """Get the cluster configuration
-
- # Gets the configuration of the cluster
-
- # :param cluster_uuid str: The UUID of the cluster.
- # :return: A dict upon success, or raises an exception.
- # """
-
- # juju_db = self.db.get_one("admin", {"_id": "juju"})
- # config = None
- # for k in juju_db["k8sclusters"]:
- # if k["_id"] == cluster_uuid:
- # config = k["config"]
- # self.db.encrypt_decrypt_fields(
- # config,
- # "decrypt",
- # ["secret", "cacert"],
- # schema_version="1.1",
- # salt=k["_id"],
- # )
- # break
- # if not config:
- # raise Exception(
- # "Unable to locate configuration for cluster {}".format(cluster_uuid)
- # )
- # return config
-
- # async def get_model(self, model_name: str, controller: Controller) -> Model:
- # """Get a model from the Juju Controller.
-
- # Note: Model objects returned must call disconnected() before it goes
- # out of scope.
-
- # :param model_name str: The name of the model to get
- # :param controller Controller: Controller object
- # :return The juju.model.Model object if found, or None.
- # """
-
- # models = await controller.list_models()
- # if model_name not in models:
- # raise N2VCNotFound("Model {} not found".format(model_name))
- # self.log.debug("Found model: {}".format(model_name))
- # return await controller.get_model(model_name)
-
def get_namespace(
self,
cluster_uuid: str,
:param cluster_uuid str: The UUID of the cluster
:returns: The namespace UUID, or raises an exception
"""
- # config = self.get_config(cluster_uuid)
-
- # Make sure the name is in the config
- # if "namespace" not in config:
- # raise Exception("Namespace not found.")
-
- # TODO: We want to make sure this is unique to the cluster, in case
- # the cluster is being reused.
- # Consider pre/appending the cluster id to the namespace string
pass
- # TODO: Remove these lines of code
- # async def has_model(self, model_name: str) -> bool:
- # """Check if a model exists in the controller
-
- # Checks to see if a model exists in the connected Juju controller.
-
- # :param model_name str: The name of the model
- # :return: A boolean indicating if the model exists
- # """
- # models = await self.controller.list_models()
-
- # if model_name in models:
- # return True
- # return False
-
- # def is_local_k8s(self, credentials: str,) -> bool:
- # """Check if a cluster is local
-
- # Checks if a cluster is running in the local host
-
- # :param credentials dict: A dictionary containing the k8s credentials
- # :returns: A boolean if the cluster is running locally
- # """
-
- # creds = yaml.safe_load(credentials)
-
- # if creds and os.getenv("OSMLCM_VCA_APIPROXY"):
- # for cluster in creds["clusters"]:
- # if "server" in cluster["cluster"]:
- # if os.getenv("OSMLCM_VCA_APIPROXY") in cluster["cluster"]["server"]:
- # return True
-
- # return False
-
- # async def get_controller(self, cluster_uuid):
- # """Login to the Juju controller."""
-
- # config = self.get_config(cluster_uuid)
-
- # juju_endpoint = config["endpoint"]
- # juju_user = config["username"]
- # juju_secret = config["secret"]
- # juju_ca_cert = config["cacert"]
-
- # controller = Controller()
-
- # if juju_secret:
- # self.log.debug(
- # "Connecting to controller... ws://{} as {}".format(
- # juju_endpoint, juju_user,
- # )
- # )
- # try:
- # await controller.connect(
- # endpoint=juju_endpoint,
- # username=juju_user,
- # password=juju_secret,
- # cacert=juju_ca_cert,
- # )
- # self.log.debug("JujuApi: Logged into controller")
- # return controller
- # except Exception as ex:
- # self.log.debug(ex)
- # self.log.debug("Caught exception: {}".format(ex))
- # else:
- # self.log.fatal("VCA credentials not configured.")
-
- # TODO: Remove these commented lines
- # self.authenticated = False
- # if self.authenticated:
- # return
-
- # self.connecting = True
- # juju_public_key = None
- # self.authenticated = True
- # Test: Make sure we have the credentials loaded
- # async def logout(self):
- # """Logout of the Juju controller."""
- # self.log.debug("[logout]")
- # if not self.authenticated:
- # return False
-
- # for model in self.models:
- # self.log.debug("Logging out of model {}".format(model))
- # await self.models[model].disconnect()
-
- # if self.controller:
- # self.log.debug("Disconnecting controller {}".format(self.controller))
- # await self.controller.disconnect()
- # self.controller = None
-
- # self.authenticated = False
-
- # async def remove_cloud(self, cloud_name: str,) -> bool:
- # """Remove a k8s cloud from Juju
-
- # Removes a Kubernetes cloud from Juju.
-
- # :param cloud_name str: The name of the cloud to add.
-
- # :returns: True if successful, otherwise raises an exception.
- # """
-
- # # Remove the bootstrapped controller
- # cmd = [self.juju_command, "remove-k8s", "--client", cloud_name]
- # process = await asyncio.create_subprocess_exec(
- # *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
- # )
-
- # _stdout, stderr = await process.communicate()
-
- # return_code = process.returncode
-
- # if return_code > 0:
- # raise Exception(stderr)
-
- # # Remove the cloud from the local config
- # cmd = [self.juju_command, "remove-cloud", "--client", cloud_name]
- # process = await asyncio.create_subprocess_exec(
- # *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
- # )
-
- # _stdout, stderr = await process.communicate()
-
- # return_code = process.returncode
-
- # if return_code > 0:
- # raise Exception(stderr)
-
- # return True
-
- # async def set_config(self, cluster_uuid: str, config: dict,) -> bool:
- # """Save the cluster configuration
-
- # Saves the cluster information to the Mongo database
-
- # :param cluster_uuid str: The UUID of the cluster
- # :param config dict: A dictionary containing the cluster configuration
- # """
-
- # juju_db = self.db.get_one("admin", {"_id": "juju"})
-
- # k8sclusters = juju_db["k8sclusters"] if "k8sclusters" in juju_db else []
- # self.db.encrypt_decrypt_fields(
- # config,
- # "encrypt",
- # ["secret", "cacert"],
- # schema_version="1.1",
- # salt=cluster_uuid,
- # )
- # k8sclusters.append({"_id": cluster_uuid, "config": config})
- # self.db.set_one(
- # table="admin",
- # q_filter={"_id": "juju"},
- # update_dict={"k8sclusters": k8sclusters},
- # )
-
- # Private methods to create/delete needed resources in the
- # Kubernetes cluster to create the K8s cloud in Juju
-
def _create_cluster_role(
self,
kubectl: Kubectl,
machine = model.machines[machine_id]
await machine.destroy(force=True)
- # async def destroy_machine(
- # self, model: Model, machine_id: str, total_timeout: float = 3600
- # ):
- # """
- # Destroy machine
-
- # :param: model: Model object
- # :param: machine_id: Machine id
- # :param: total_timeout: Timeout in seconds
- # """
- # machines = await model.get_machines()
- # if machine_id in machines:
- # machine = machines[machine_id]
- # await machine.destroy(force=True)
- # # max timeout
- # end = time.time() + total_timeout
-
- # # wait for machine removal
- # machines = await model.get_machines()
- # while machine_id in machines and time.time() < end:
- # self.log.debug("Waiting for machine {} is destroyed".format(machine_id))
- # await asyncio.sleep(0.5)
- # machines = await model.get_machines()
- # self.log.debug("Machine destroyed: {}".format(machine_id))
- # else:
- # self.log.debug("Machine not found: {}".format(machine_id))
-
async def configure_application(
self, model_name: str, application_name: str, config: dict = None
):
).format(ee_id, application_name, e)
)
- # destroy the machine
- # try:
- # await self._juju_destroy_machine(
- # model_name=model_name,
- # machine_id=machine_id,
- # total_timeout=total_timeout
- # )
- # except Exception as e:
- # raise N2VCException(
- # message='Error deleting execution environment {} (machine {}) : {}'
- # .format(ee_id, machine_id, e))
-
self.log.info("Execution environment {} deleted".format(ee_id))
async def exec_primitive(
+++ /dev/null
-<!--
- Copyright 2019 Canonical Ltd.
-
- 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.
--->
-
-# N2VC Testing
-
-
-# Preparation
-## Environment variables
-
-The test currently requires some environment variables set in order to run, but these will be deprecated as soon as possible.
-
-## LXD
-
-LXD should be installed via snap.
-
-The connection to the LXD API server expects to use a self-signed SSL certificate, generated by lxc (`lxc list`, et al) is first one.
-
-## Juju
-
-Juju is expected to be installed via snap and bootstrapped.
-
-Run `juju status -m controller` and capture the IP address of machine 0. This is the Juju controller, specified in VCA_HOST
-
-export VCA_HOST=1.2.3.4
-export VCA_USER=admin
-export VCA_SECRET=admin
-export VCA_CACERT=$(juju controllers --format json | jq -r '.controllers["osm"]["ca-cert"]'| base64 | tr -d \\n)
-export VCA_PUBLIC_KEY=$(cat ~/.local/share/juju/ssh/juju_id_rsa.pub)
-
-# Running tests
-
-Tests are written with pytest, driven by tox. All tests are run from the root directory of the repository.
-
-## Run one test
-
-To run a single integration test, we tell tox which environment we need, and then the path to the test.
-
-```bash
-tox -e integration -- tests/integration/test_non-string_parameter.py
-```
-
-## Running all tests
-
-`make test` will invoke tox to run all unit tests. Alternatively, you can limit this to a specific type of test by invoking tox manually:
-```bash
-tox -e integration -- tests/integration/
-```
-
-# TODO
-- Update CI environment to have Juju and LXD available via snap
-- Investigate running via Docker
-- Remove the requirement for setting environment variables
-- Integrate into Jenkins so that tests run against every commit
-- Add global timeout to abort tests that are hung
-- Only build a charm once per test run, i.e., if two or more tests use the same charm, we should only call `charm build` once.
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-
-# 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.
-
+++ /dev/null
-#!/usr/bin/env python3
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import asyncio
-import datetime
-import logging
-import n2vc.vnf
-import pylxd
-import pytest
-import os
-import shlex
-import subprocess
-import time
-import uuid
-import yaml
-
-from juju.controller import Controller
-
-# Disable InsecureRequestWarning w/LXD
-import urllib3
-urllib3.disable_warnings()
-logging.getLogger("urllib3").setLevel(logging.WARNING)
-
-here = os.path.dirname(os.path.realpath(__file__))
-
-
-class CleanController():
- """
- Context manager that automatically connects and disconnects from
- the currently active controller.
-
- Note: Unlike CleanModel, this will not create a new controller for you,
- and an active controller must already be available.
- """
- def __init__(self):
- self._controller = None
-
- async def __aenter__(self):
- self._controller = Controller()
- await self._controller.connect()
- return self._controller
-
- async def __aexit__(self, exc_type, exc, tb):
- await self._controller.disconnect()
-
-
-def debug(msg):
- """Format debug messages in a consistent way."""
- now = datetime.datetime.now()
-
- # TODO: Decide on the best way to log. Output from `logging.debug` shows up
- # when a test fails, but print() will always show up when running tox with
- # `-s`, which is really useful for debugging single tests without having to
- # insert a False assert to see the log.
- logging.debug(
- "[{}] {}".format(now.strftime('%Y-%m-%dT%H:%M:%S'), msg)
- )
- print(
- "[{}] {}".format(now.strftime('%Y-%m-%dT%H:%M:%S'), msg)
- )
-
-
-def get_charm_path():
- return "{}/charms".format(here)
-
-
-def get_layer_path():
- return "{}/charms/layers".format(here)
-
-
-def collect_metrics(application):
- """Invoke Juju's metrics collector.
-
- Caveat: this shells out to the `juju collect-metrics` command, rather than
- making an API call. At the time of writing, that API is not exposed through
- the client library.
- """
-
- try:
- subprocess.check_call(['juju', 'collect-metrics', application])
- except subprocess.CalledProcessError as e:
- raise Exception("Unable to collect metrics: {}".format(e))
-
-
-def has_metrics(charm):
- """Check if a charm has metrics defined."""
- metricsyaml = "{}/{}/metrics.yaml".format(
- get_layer_path(),
- charm,
- )
- if os.path.exists(metricsyaml):
- return True
- return False
-
-
-def get_descriptor(descriptor):
- desc = None
- try:
- tmp = yaml.safe_load(descriptor)
-
- # Remove the envelope
- root = list(tmp.keys())[0]
- if root == "nsd:nsd-catalog":
- desc = tmp['nsd:nsd-catalog']['nsd'][0]
- elif root == "vnfd:vnfd-catalog":
- desc = tmp['vnfd:vnfd-catalog']['vnfd'][0]
- except ValueError:
- assert False
- return desc
-
-
-def get_n2vc(loop=None):
- """Return an instance of N2VC.VNF."""
- log = logging.getLogger()
- log.level = logging.DEBUG
-
- # Extract parameters from the environment in order to run our test
- vca_host = os.getenv('VCA_HOST', '127.0.0.1')
- vca_port = os.getenv('VCA_PORT', 17070)
- vca_user = os.getenv('VCA_USER', 'admin')
- vca_charms = os.getenv('VCA_CHARMS', None)
- vca_secret = os.getenv('VCA_SECRET', None)
- vca_cacert = os.getenv('VCA_CACERT', None)
-
- # Get the Juju Public key
- juju_public_key = get_juju_public_key()
- if juju_public_key:
- debug("Reading Juju public key @ {}".format(juju_public_key))
- with open(juju_public_key, 'r') as f:
- juju_public_key = f.read()
- debug("Found public key: {}".format(juju_public_key))
- else:
- raise Exception("No Juju Public Key found")
-
- # Get the ca-cert
- # os.path.expanduser("~/.config/lxc")
- # with open("{}/agent.conf".format(AGENT_PATH), "r") as f:
- # try:
- # y = yaml.safe_load(f)
- # self.cacert = y['cacert']
- # except yaml.YAMLError as exc:
- # log("Unable to find Juju ca-cert.")
- # raise exc
-
- client = n2vc.vnf.N2VC(
- log=log,
- server=vca_host,
- port=vca_port,
- user=vca_user,
- secret=vca_secret,
- artifacts=vca_charms,
- loop=loop,
- juju_public_key=juju_public_key,
- ca_cert=vca_cacert,
- )
- return client
-
-
-def create_lxd_container(public_key=None, name="test_name"):
- """
- Returns a container object
-
- If public_key isn't set, we'll use the Juju ssh key
-
- :param public_key: The public key to inject into the container
- :param name: The name of the test being run
- """
- container = None
-
- # Format name so it's valid
- name = name.replace("_", "-").replace(".", "")
-
- client = get_lxd_client()
- if not client:
- raise Exception("Unable to connect to LXD")
-
- test_machine = "test-{}-{}".format(
- uuid.uuid4().hex[-4:],
- name,
- )
-
- private_key_path, public_key_path = find_n2vc_ssh_keys()
-
- try:
- # create profile w/cloud-init and juju ssh key
- if not public_key:
- public_key = ""
- with open(public_key_path, "r") as f:
- public_key = f.readline()
-
- client.profiles.create(
- test_machine,
- config={
- 'user.user-data': '#cloud-config\nssh_authorized_keys:\n- {}'.format(public_key)},
- devices={
- 'root': {'path': '/', 'pool': 'default', 'type': 'disk'},
- 'eth0': {
- 'nictype': 'bridged',
- 'parent': 'lxdbr0',
- 'type': 'nic'
- }
- }
- )
- except Exception as ex:
- debug("Error creating lxd profile {}: {}".format(test_machine, ex))
- raise ex
-
- try:
- # create lxc machine
- config = {
- 'name': test_machine,
- 'source': {
- 'type': 'image',
- 'alias': 'xenial',
- 'mode': 'pull',
- 'protocol': 'simplestreams',
- 'server': 'https://cloud-images.ubuntu.com/releases',
- },
- 'profiles': [test_machine],
- }
- container = client.containers.create(config, wait=True)
- container.start(wait=True)
- except Exception as ex:
- debug("Error creating lxd container {}: {}".format(test_machine, ex))
- # This is a test-ending failure.
- raise ex
-
- def wait_for_network(container, timeout=30):
- """Wait for eth0 to have an ipv4 address."""
- starttime = time.time()
- while(time.time() < starttime + timeout):
- time.sleep(1)
- if 'eth0' in container.state().network:
- addresses = container.state().network['eth0']['addresses']
- if len(addresses) > 0:
- if addresses[0]['family'] == 'inet':
- return addresses[0]
- return None
-
- try:
- wait_for_network(container)
- except Exception as ex:
- debug(
- "Error waiting for container {} network: {}".format(
- test_machine,
- ex,
- )
- )
-
- try:
- waitcount = 0
- while waitcount <= 5:
- if is_sshd_running(container):
- break
- waitcount += 1
- time.sleep(1)
- if waitcount >= 5:
- debug("couldn't detect sshd running")
- raise Exception("Unable to verify container sshd")
-
- except Exception as ex:
- debug(
- "Error checking sshd status on {}: {}".format(
- test_machine,
- ex,
- )
- )
-
- # HACK: We need to give sshd a chance to bind to the interface,
- # and pylxd's container.execute seems to be broken and fails and/or
- # hangs trying to properly check if the service is up.
- (exit_code, stdout, stderr) = container.execute([
- 'ping',
- '-c', '5', # Wait for 5 ECHO_REPLY
- '8.8.8.8', # Ping Google's public DNS
- '-W', '15', # Set a 15 second deadline
- ])
- if exit_code > 0:
- # The network failed
- raise Exception("Unable to verify container network")
-
- return container
-
-
-def is_sshd_running(container):
- """Check if sshd is running in the container.
-
- Check to see if the sshd process is running and listening on port 22.
-
- :param container: The container to check
- :return boolean: True if sshd is running.
- """
- debug("Container: {}".format(container))
- try:
- (rc, stdout, stderr) = container.execute(
- ["service", "ssh", "status"]
- )
- # If the status is a) found and b) running, the exit code will be 0
- if rc == 0:
- return True
- except Exception as ex:
- debug("Failed to check sshd service status: {}".format(ex))
-
- return False
-
-
-def destroy_lxd_container(container):
- """Stop and delete a LXD container.
-
- Sometimes we see errors talking to LXD -- ephemerial issues like
- load or a bug that's killed the API. We'll do our best to clean
- up here, and we should run a cleanup after all tests are finished
- to remove any extra containers and profiles belonging to us.
- """
-
- if type(container) is bool:
- return
-
- name = container.name
- debug("Destroying container {}".format(name))
-
- client = get_lxd_client()
-
- def wait_for_stop(timeout=30):
- """Wait for eth0 to have an ipv4 address."""
- starttime = time.time()
- while(time.time() < starttime + timeout):
- time.sleep(1)
- if container.state == "Stopped":
- return
-
- def wait_for_delete(timeout=30):
- starttime = time.time()
- while(time.time() < starttime + timeout):
- time.sleep(1)
- if client.containers.exists(name) is False:
- return
-
- try:
- container.stop(wait=False)
- wait_for_stop()
- except Exception as ex:
- debug(
- "Error stopping container {}: {}".format(
- name,
- ex,
- )
- )
-
- try:
- container.delete(wait=False)
- wait_for_delete()
- except Exception as ex:
- debug(
- "Error deleting container {}: {}".format(
- name,
- ex,
- )
- )
-
- try:
- # Delete the profile created for this container
- profile = client.profiles.get(name)
- if profile:
- profile.delete()
- except Exception as ex:
- debug(
- "Error deleting profile {}: {}".format(
- name,
- ex,
- )
- )
-
-
-def find_lxd_config():
- """Find the LXD configuration directory."""
- paths = []
- paths.append(os.path.expanduser("~/.config/lxc"))
- paths.append(os.path.expanduser("~/snap/lxd/current/.config/lxc"))
-
- for path in paths:
- if os.path.exists(path):
- crt = os.path.expanduser("{}/client.crt".format(path))
- key = os.path.expanduser("{}/client.key".format(path))
- if os.path.exists(crt) and os.path.exists(key):
- return (crt, key)
- return (None, None)
-
-
-def find_n2vc_ssh_keys():
- """Find the N2VC ssh keys."""
-
- paths = []
- paths.append(os.path.expanduser("~/.ssh/"))
-
- for path in paths:
- if os.path.exists(path):
- private = os.path.expanduser("{}/id_n2vc_rsa".format(path))
- public = os.path.expanduser("{}/id_n2vc_rsa.pub".format(path))
- if os.path.exists(private) and os.path.exists(public):
- return (private, public)
- return (None, None)
-
-
-def find_juju_ssh_keys():
- """Find the Juju ssh keys."""
-
- paths = []
- paths.append(os.path.expanduser("~/.local/share/juju/ssh"))
-
- for path in paths:
- if os.path.exists(path):
- private = os.path.expanduser("{}/juju_id_rsa".format(path))
- public = os.path.expanduser("{}/juju_id_rsa.pub".format(path))
- if os.path.exists(private) and os.path.exists(public):
- return (private, public)
- return (None, None)
-
-
-def get_juju_private_key():
- keys = find_juju_ssh_keys()
- return keys[0]
-
-
-def get_juju_public_key():
- """Find the Juju public key."""
- paths = []
-
- if 'VCA_PATH' in os.environ:
- paths.append("{}/ssh".format(os.environ["VCA_PATH"]))
-
- paths.append(os.path.expanduser("~/.local/share/juju/ssh"))
- paths.append("/root/.local/share/juju/ssh")
-
- for path in paths:
- if os.path.exists(path):
- public = os.path.expanduser("{}/juju_id_rsa.pub".format(path))
- if os.path.exists(public):
- return public
- return None
-
-
-def get_lxd_client(host=None, port="8443", verify=False):
- """ Get the LXD client."""
-
- if host is None:
- if 'LXD_HOST' in os.environ:
- host = os.environ['LXD_HOST']
- else:
- host = '127.0.0.1'
-
- passwd = None
- if 'LXD_SECRET' in os.environ:
- passwd = os.environ['LXD_SECRET']
-
- # debug("Connecting to LXD remote {} w/authentication ({})".format(
- # host,
- # passwd
- # ))
- client = None
- (crt, key) = find_lxd_config()
-
- if crt and key:
- client = pylxd.Client(
- endpoint="https://{}:{}".format(host, port),
- cert=(crt, key),
- verify=verify,
- )
-
- # If the LXD server has a pasword set, authenticate with it.
- if not client.trusted and passwd:
- try:
- client.authenticate(passwd)
- if not client.trusted:
- raise Exception("Unable to authenticate with LXD remote")
- except pylxd.exceptions.LXDAPIException as ex:
- if 'Certificate already in trust store' in ex:
- pass
-
- return client
-
-
-# TODO: This is marked serial but can be run in parallel with work, including:
-# - Fixing an event loop issue; seems that all tests stop when one test stops?
-
-
-@pytest.mark.serial
-class TestN2VC(object):
- """TODO:
- 1. Validator Validation
-
- Automatically validate the descriptors we're using here, unless the test
- author explicitly wants to skip them. Useful to make sure tests aren't
- being run against invalid descriptors, validating functionality that may
- fail against a properly written descriptor.
-
- We need to have a flag (instance variable) that controls this behavior. It
- may be necessary to skip validation and run against a descriptor
- implementing features that have not yet been released in the Information
- Model.
- """
-
- """
- The six phases of integration testing, for the test itself and each charm?:
-
- setup/teardown_class:
- 1. Prepare - Verify the environment and create a new model
- 2. Deploy - Mark the test as ready to execute
- 3. Configure - Configuration to reach Active state
- 4. Test - Execute primitive(s) to verify success
- 5. Collect - Collect any useful artifacts for debugging (charm, logs)
- 6. Destroy - Destroy the model
-
-
- 1. Prepare - Building of charm
- 2. Deploy - Deploying charm
- 3. Configure - Configuration to reach Active state
- 4. Test - Execute primitive(s) to verify success
- 5. Collect - Collect any useful artifacts for debugging (charm, logs)
- 6. Destroy - Destroy the charm
-
- """
- @classmethod
- def setup_class(self):
- """ setup any state specific to the execution of the given class (which
- usually contains tests).
- """
- # Initialize instance variable(s)
- self.n2vc = None
-
- # Track internal state for each test run
- self.state = {}
-
- # Parse the test's descriptors
- self.nsd = get_descriptor(self.NSD_YAML)
- self.vnfd = get_descriptor(self.VNFD_YAML)
-
- self.ns_name = self.nsd['name']
- self.vnf_name = self.vnfd['name']
-
- self.charms = {}
- self.parse_vnf_descriptor()
- assert self.charms is not {}
-
- # Track artifacts, like compiled charms, that will need to be removed
- self.artifacts = {}
-
- # Build the charm(s) needed for this test
- for charm in self.get_charm_names():
- # debug("Building charm {}".format(charm))
- self.get_charm(charm)
-
- # A bit of a hack, in order to allow the N2VC callback to run parallel
- # to pytest. Test(s) should wait for this flag to change to False
- # before returning.
- self._running = True
- self._stopping = False
-
- @classmethod
- def teardown_class(self):
- """ teardown any state that was previously setup with a call to
- setup_class.
- """
- debug("Running teardown_class...")
- try:
-
- debug("Destroying LXD containers...")
- for application in self.state:
- if self.state[application]['container']:
- destroy_lxd_container(self.state[application]['container'])
- debug("Destroying LXD containers...done.")
-
- # Logout of N2VC
- if self.n2vc:
- debug("teardown_class(): Logging out of N2VC...")
- yield from self.n2vc.logout()
- debug("teardown_class(): Logging out of N2VC...done.")
-
- debug("Running teardown_class...done.")
- except Exception as ex:
- debug("Exception in teardown_class: {}".format(ex))
-
- @classmethod
- def all_charms_active(self):
- """Determine if the all deployed charms are active."""
- active = 0
-
- for application in self.state:
- if 'status' in self.state[application]:
- debug("status of {} is '{}'".format(
- application,
- self.state[application]['status'],
- ))
- if self.state[application]['status'] == 'active':
- active += 1
-
- debug("Active charms: {}/{}".format(
- active,
- len(self.charms),
- ))
-
- if active == len(self.charms):
- return True
-
- return False
-
- @classmethod
- def are_tests_finished(self):
- appcount = len(self.state)
-
- # If we don't have state yet, keep running.
- if appcount == 0:
- debug("No applications")
- return False
-
- if self._stopping:
- debug("_stopping is True")
- return True
-
- appdone = 0
- for application in self.state:
- if self.state[application]['done']:
- appdone += 1
-
- debug("{}/{} charms tested".format(appdone, appcount))
-
- if appcount == appdone:
- return True
-
- return False
-
- @classmethod
- async def running(self, timeout=600):
- """Returns if the test is still running.
-
- @param timeout The time, in seconds, to wait for the test to complete.
- """
- if self.are_tests_finished():
- await self.stop()
- return False
-
- await asyncio.sleep(30)
-
- return self._running
-
- @classmethod
- def get_charm(self, charm):
- """Build and return the path to the test charm.
-
- Builds one of the charms in tests/charms/layers and returns the path
- to the compiled charm. The charm will automatically be removed when
- when the test is complete.
-
- Returns: The path to the built charm or None if `charm build` failed.
- """
- # Make sure the charm snap is installed
- charm_cmd = None
- try:
- subprocess.check_call(['which', 'charm'])
- charm_cmd = "charm build"
- except subprocess.CalledProcessError:
- # charm_cmd = "charm-build"
- # debug("Using legacy charm-build")
- raise Exception("charm snap not installed.")
-
- if charm not in self.artifacts:
- try:
- # Note: This builds the charm under N2VC/tests/charms/builds/
- # Currently, the snap-installed command only has write access
- # to the $HOME (changing in an upcoming release) so writing to
- # /tmp isn't possible at the moment.
-
- builds = get_charm_path()
- if not os.path.exists("{}/builds/{}".format(builds, charm)):
- cmd = "{} --no-local-layers {}/{} -o {}/".format(
- charm_cmd,
- get_layer_path(),
- charm,
- builds,
- )
- # debug(cmd)
-
- env = os.environ.copy()
- env["CHARM_BUILD_DIR"] = builds
-
- subprocess.check_call(shlex.split(cmd), env=env)
-
- except subprocess.CalledProcessError as e:
- # charm build will return error code 100 if the charm fails
- # the auto-run of charm proof, which we can safely ignore for
- # our CI charms.
- if e.returncode != 100:
- raise Exception("charm build failed: {}.".format(e))
-
- self.artifacts[charm] = {
- 'tmpdir': builds,
- 'charm': "{}/builds/{}".format(builds, charm),
- }
-
- return self.artifacts[charm]['charm']
-
- @classmethod
- async def deploy(self, vnf_index, charm, params, loop):
- """An inner function to do the deployment of a charm from
- either a vdu or vnf.
- """
-
- if not self.n2vc:
- self.n2vc = get_n2vc(loop=loop)
-
- debug("Creating model for Network Service {}".format(self.ns_name))
- await self.n2vc.CreateNetworkService(self.ns_name)
-
- application = self.n2vc.FormatApplicationName(
- self.ns_name,
- self.vnf_name,
- str(vnf_index),
- )
-
- # Initialize the state of the application
- self.state[application] = {
- 'status': None, # Juju status
- 'container': None, # lxd container, for proxy charms
- 'actions': {}, # Actions we've executed
- 'done': False, # Are we done testing this charm?
- 'phase': "deploy", # What phase is this application in?
- }
-
- debug("Deploying charm at {}".format(self.artifacts[charm]))
-
- # If this is a native charm, we need to provision the underlying
- # machine ala an LXC container.
- machine_spec = {}
-
- if not self.isproxy(application):
- debug("Creating container for native charm")
- # args = ("default", application, None, None)
- self.state[application]['container'] = create_lxd_container(
- name=os.path.basename(__file__)
- )
-
- hostname = self.get_container_ip(
- self.state[application]['container'],
- )
-
- machine_spec = {
- 'hostname': hostname,
- 'username': 'ubuntu',
- }
-
- await self.n2vc.DeployCharms(
- self.ns_name,
- application,
- self.vnfd,
- self.get_charm(charm),
- params,
- machine_spec,
- self.n2vc_callback,
- )
-
- @classmethod
- def parse_vnf_descriptor(self):
- """Parse the VNF descriptor to make running tests easier.
-
- Parse the charm information in the descriptor to make it easy to write
- tests to run again it.
-
- Each charm becomes a dictionary in a list:
- [
- 'is-proxy': True,
- 'vnf-member-index': 1,
- 'vnf-name': '',
- 'charm-name': '',
- 'initial-config-primitive': {},
- 'config-primitive': {}
- ]
- - charm name
- - is this a proxy charm?
- - what are the initial-config-primitives (day 1)?
- - what are the config primitives (day 2)?
-
- """
- charms = {}
-
- # You'd think this would be explicit, but it's just an incremental
- # value that should be consistent.
- vnf_member_index = 0
-
- """Get all vdu and/or vdu config in a descriptor."""
- config = self.get_config()
- for cfg in config:
- if 'juju' in cfg:
-
- # Get the name to be used for the deployed application
- application_name = n2vc.vnf.N2VC().FormatApplicationName(
- self.ns_name,
- self.vnf_name,
- str(vnf_member_index),
- )
-
- charm = {
- 'application-name': application_name,
- 'proxy': True,
- 'vnf-member-index': vnf_member_index,
- 'vnf-name': self.vnf_name,
- 'name': None,
- 'initial-config-primitive': {},
- 'config-primitive': {},
- }
-
- juju = cfg['juju']
- charm['name'] = juju['charm']
-
- if 'proxy' in juju:
- charm['proxy'] = juju['proxy']
-
- if 'initial-config-primitive' in cfg:
- charm['initial-config-primitive'] = \
- cfg['initial-config-primitive']
-
- if 'config-primitive' in cfg:
- charm['config-primitive'] = cfg['config-primitive']
-
- charms[application_name] = charm
-
- # Increment the vnf-member-index
- vnf_member_index += 1
-
- self.charms = charms
-
- @classmethod
- def isproxy(self, application_name):
-
- assert application_name in self.charms
- assert 'proxy' in self.charms[application_name]
- assert type(self.charms[application_name]['proxy']) is bool
-
- # debug(self.charms[application_name])
- return self.charms[application_name]['proxy']
-
- @classmethod
- def get_config(self):
- """Return an iterable list of config items (vdu and vnf).
-
- As far as N2VC is concerned, the config section for vdu and vnf are
- identical. This joins them together so tests only need to iterate
- through one list.
- """
- configs = []
-
- """Get all vdu and/or vdu config in a descriptor."""
- vnf_config = self.vnfd.get("vnf-configuration")
- if vnf_config:
- juju = vnf_config['juju']
- if juju:
- configs.append(vnf_config)
-
- for vdu in self.vnfd['vdu']:
- vdu_config = vdu.get('vdu-configuration')
- if vdu_config:
- juju = vdu_config['juju']
- if juju:
- configs.append(vdu_config)
-
- return configs
-
- @classmethod
- def get_charm_names(self):
- """Return a list of charms used by the test descriptor."""
-
- charms = {}
-
- # Check if the VDUs in this VNF have a charm
- for config in self.get_config():
- juju = config['juju']
-
- name = juju['charm']
- if name not in charms:
- charms[name] = 1
-
- return charms.keys()
-
- @classmethod
- def get_phase(self, application):
- return self.state[application]['phase']
-
- @classmethod
- def set_phase(self, application, phase):
- self.state[application]['phase'] = phase
-
- @classmethod
- async def configure_proxy_charm(self, *args):
- """Configure a container for use via ssh."""
- (model, application, _, _) = args
-
- try:
- if self.get_phase(application) == "deploy":
- self.set_phase(application, "configure")
-
- debug("Start CreateContainer for {}".format(application))
- self.state[application]['container'] = \
- await self.CreateContainer(*args)
- debug("Done CreateContainer for {}".format(application))
-
- if self.state[application]['container']:
- debug("Configure {} for container".format(application))
- if await self.configure_ssh_proxy(application):
- await asyncio.sleep(0.1)
- return True
- else:
- debug("Failed to configure container for {}".format(application))
- else:
- debug("skipping CreateContainer for {}: {}".format(
- application,
- self.get_phase(application),
- ))
-
- except Exception as ex:
- debug("configure_proxy_charm exception: {}".format(ex))
- finally:
- await asyncio.sleep(0.1)
-
- return False
-
- @classmethod
- async def execute_charm_tests(self, *args):
- (model, application, _, _) = args
-
- debug("Executing charm test(s) for {}".format(application))
-
- if self.state[application]['done']:
- debug("Trying to execute tests against finished charm...aborting")
- return False
-
- try:
- phase = self.get_phase(application)
- # We enter the test phase when after deploy (for native charms) or
- # configure, for proxy charms.
- if phase in ["deploy", "configure"]:
- self.set_phase(application, "test")
- if self.are_tests_finished():
- raise Exception("Trying to execute init-config on finished test")
-
- if await self.execute_initial_config_primitives(application):
- # check for metrics
- await self.check_metrics(application)
-
- debug("Done testing {}".format(application))
- self.state[application]['done'] = True
-
- except Exception as ex:
- debug("Exception in execute_charm_tests: {}".format(ex))
- finally:
- await asyncio.sleep(0.1)
-
- return True
-
- @classmethod
- async def CreateContainer(self, *args):
- """Create a LXD container for use with a proxy charm.abs
-
- 1. Get the public key from the charm via `get-ssh-public-key` action
- 2. Create container with said key injected for the ubuntu user
-
- Returns a Container object
- """
- # Create and configure a LXD container for use with a proxy charm.
- (model, application, _, _) = args
-
- debug("[CreateContainer] {}".format(args))
- container = None
-
- try:
- # Execute 'get-ssh-public-key' primitive and get returned value
- uuid = await self.n2vc.ExecutePrimitive(
- model,
- application,
- "get-ssh-public-key",
- None,
- )
-
- result = await self.n2vc.GetPrimitiveOutput(model, uuid)
- pubkey = result['pubkey']
-
- container = create_lxd_container(
- public_key=pubkey,
- name=os.path.basename(__file__)
- )
-
- return container
- except Exception as ex:
- debug("Error creating container: {}".format(ex))
- pass
-
- return None
-
- @classmethod
- async def stop(self):
- """Stop the test.
-
- - Remove charms
- - Stop and delete containers
- - Logout of N2VC
-
- TODO: Clean up duplicate code between teardown_class() and stop()
- """
- debug("stop() called")
-
- if self.n2vc and self._running and not self._stopping:
- self._running = False
- self._stopping = True
-
- # Destroy the network service
- try:
- await self.n2vc.DestroyNetworkService(self.ns_name)
- except Exception as e:
- debug(
- "Error Destroying Network Service \"{}\": {}".format(
- self.ns_name,
- e,
- )
- )
-
- # Wait for the applications to be removed and delete the containers
- for application in self.charms:
- try:
-
- while True:
- # Wait for the application to be removed
- await asyncio.sleep(10)
- if not await self.n2vc.HasApplication(
- self.ns_name,
- application,
- ):
- break
-
- # Need to wait for the charm to finish, because native charms
- if self.state[application]['container']:
- debug("Deleting LXD container...")
- destroy_lxd_container(
- self.state[application]['container']
- )
- self.state[application]['container'] = None
- debug("Deleting LXD container...done.")
- else:
- debug("No container found for {}".format(application))
- except Exception as e:
- debug("Error while deleting container: {}".format(e))
-
- # Logout of N2VC
- try:
- debug("stop(): Logging out of N2VC...")
- await self.n2vc.logout()
- self.n2vc = None
- debug("stop(): Logging out of N2VC...Done.")
- except Exception as ex:
- debug(ex)
-
- # Let the test know we're finished.
- debug("Marking test as finished.")
- # self._running = False
- else:
- debug("Skipping stop()")
-
- @classmethod
- def get_container_ip(self, container):
- """Return the IPv4 address of container's eth0 interface."""
- ipaddr = None
- if container:
- addresses = container.state().network['eth0']['addresses']
- # The interface may have more than one address, but we only need
- # the first one for testing purposes.
- ipaddr = addresses[0]['address']
-
- return ipaddr
-
- @classmethod
- async def configure_ssh_proxy(self, application, task=None):
- """Configure the proxy charm to use the lxd container.
-
- Configure the charm to use a LXD container as it's VNF.
- """
- debug("Configuring ssh proxy for {}".format(application))
-
- mgmtaddr = self.get_container_ip(
- self.state[application]['container'],
- )
-
- debug(
- "Setting ssh-hostname for {} to {}".format(
- application,
- mgmtaddr,
- )
- )
-
- await self.n2vc.ExecutePrimitive(
- self.ns_name,
- application,
- "config",
- None,
- params={
- 'ssh-hostname': mgmtaddr,
- 'ssh-username': 'ubuntu',
- }
- )
-
- return True
-
- @classmethod
- async def execute_initial_config_primitives(self, application, task=None):
- debug("Executing initial_config_primitives for {}".format(application))
- try:
- init_config = self.charms[application]
-
- """
- The initial-config-primitive is run during deploy but may fail
- on some steps because proxy charm access isn't configured.
-
- Re-run those actions so we can inspect the status.
- """
- uuids = await self.n2vc.ExecuteInitialPrimitives(
- self.ns_name,
- application,
- init_config,
- )
-
- """
- ExecutePrimitives will return a list of uuids. We need to check the
- status of each. The test continues if all Actions succeed, and
- fails if any of them fail.
- """
- await self.wait_for_uuids(application, uuids)
- debug("Primitives for {} finished.".format(application))
-
- return True
- except Exception as ex:
- debug("execute_initial_config_primitives exception: {}".format(ex))
- raise ex
-
- return False
-
- @classmethod
- async def check_metrics(self, application, task=None):
- """Check and run metrics, if present.
-
- Checks to see if metrics are specified by the charm. If so, collects
- the metrics.
-
- If no metrics, then mark the test as finished.
- """
- if has_metrics(self.charms[application]['name']):
- debug("Collecting metrics for {}".format(application))
-
- metrics = await self.n2vc.GetMetrics(
- self.ns_name,
- application,
- )
-
- return await self.verify_metrics(application, metrics)
-
- @classmethod
- async def verify_metrics(self, application, metrics):
- """Verify the charm's metrics.
-
- Verify that the charm has sent metrics successfully.
-
- Stops the test when finished.
- """
- debug("Verifying metrics for {}: {}".format(application, metrics))
-
- if len(metrics):
- return True
-
- else:
- # TODO: Ran into a case where it took 9 attempts before metrics
- # were available; the controller is slow sometimes.
- await asyncio.sleep(30)
- return await self.check_metrics(application)
-
- @classmethod
- async def wait_for_uuids(self, application, uuids):
- """Wait for primitives to execute.
-
- The task will provide a list of uuids representing primitives that are
- queued to run.
- """
- debug("Waiting for uuids for {}: {}".format(application, uuids))
- waitfor = len(uuids)
- finished = 0
-
- while waitfor > finished:
- for uid in uuids:
- await asyncio.sleep(10)
-
- if uuid not in self.state[application]['actions']:
- self.state[application]['actions'][uid] = "pending"
-
- status = self.state[application]['actions'][uid]
-
- # Have we already marked this as done?
- if status in ["pending", "running"]:
-
- debug("Getting status of {} ({})...".format(uid, status))
- status = await self.n2vc.GetPrimitiveStatus(
- self.ns_name,
- uid,
- )
- debug("...state of {} is {}".format(uid, status))
- self.state[application]['actions'][uid] = status
-
- if status in ['completed', 'failed']:
- finished += 1
-
- debug("{}/{} actions complete".format(finished, waitfor))
-
- # Wait for the primitive to finish and try again
- if waitfor > finished:
- debug("Waiting 10s for action to finish...")
- await asyncio.sleep(10)
-
- @classmethod
- def n2vc_callback(self, *args, **kwargs):
- (model, application, status, message) = args
- # debug("callback: {}".format(args))
-
- if application not in self.state:
- # Initialize the state of the application
- self.state[application] = {
- 'status': None, # Juju status
- 'container': None, # lxd container, for proxy charms
- 'actions': {}, # Actions we've executed
- 'done': False, # Are we done testing this charm?
- 'phase': "deploy", # What phase is this application in?
- }
-
- self.state[application]['status'] = status
-
- if status in ['waiting', 'maintenance', 'unknown']:
- # Nothing to do for these
- return
-
- debug("callback: {}".format(args))
-
- if self.state[application]['done']:
- debug("{} is done".format(application))
- return
-
- if status in ['error']:
- # To test broken charms, if a charm enters an error state we should
- # end the test
- debug("{} is in an error state, stop the test.".format(application))
- # asyncio.ensure_future(self.stop())
- self.state[application]['done'] = True
- assert False
-
- if status in ["blocked"] and self.isproxy(application):
- if self.state[application]['phase'] == "deploy":
- debug("Configuring proxy charm for {}".format(application))
- asyncio.ensure_future(self.configure_proxy_charm(*args))
-
- elif status in ["active"]:
- """When a charm is active, we can assume that it has been properly
- configured (not blocked), regardless of if it's a proxy or not.
-
- All primitives should be complete by init_config_primitive
- """
- asyncio.ensure_future(self.execute_charm_tests(*args))
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-description: Test bundle
-bundle: kubernetes
-applications:
- zookeeper-k8s:
- charm: 'cs:~charmed-osm/zookeeper-k8s-29'
- scale: 1
- series: kubernetes
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-description: Test bundle
-bundle: kubernetes
-applications:
- zookeeper-k8s:
- charm: 'cs:~charmed-osm/zookeeper-k8s-31'
- scale: 1
- series: kubernetes
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-description: Test bundle
-bundle: kubernetes
-applications:
- zookeeper-k8s:
- charm: 'cs:~charmed-osm/zookeeper-k8s-30'
- scale: 1
- series: kubernetes
+++ /dev/null
-# Overview
-
-This charm is intended to install and break, requiring it to be removed.
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-touch:
- description: "Touch a file on the VNF."
- params:
- filename:
- description: "The name of the file to touch."
- type: string
- default: ""
- required:
- - filename
+++ /dev/null
-#!/usr/bin/env python3
-##
-# Copyright 2016 Canonical Ltd.
-# All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-##
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main, set_flag
-from charmhelpers.core.hookenv import action_fail, action_name
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_flag('actions.{}'.format(action_name()))
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-options:
- string-option:
- type: string
- default: "Default Value"
- description: "A short description of the configuration option"
- boolean-option:
- type: boolean
- default: False
- description: "A short description of the configuration option"
- int-option:
- type: int
- default: 9001
- description: "A short description of the configuration option"
-
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
-<!-- Created with Inkscape (http://www.inkscape.org/) -->\r
-\r
-<svg\r
- xmlns:dc="http://purl.org/dc/elements/1.1/"\r
- xmlns:cc="http://creativecommons.org/ns#"\r
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\r
- xmlns:svg="http://www.w3.org/2000/svg"\r
- xmlns="http://www.w3.org/2000/svg"\r
- xmlns:xlink="http://www.w3.org/1999/xlink"\r
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"\r
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"\r
- width="96"\r
- height="96"\r
- id="svg6517"\r
- version="1.1"\r
- inkscape:version="0.48+devel r12274"\r
- sodipodi:docname="Juju_charm_icon_template.svg">\r
- <defs\r
- id="defs6519">\r
- <linearGradient\r
- inkscape:collect="always"\r
- xlink:href="#Background"\r
- id="linearGradient6461"\r
- gradientUnits="userSpaceOnUse"\r
- x1="0"\r
- y1="970.29498"\r
- x2="144"\r
- y2="970.29498"\r
- gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" />\r
- <linearGradient\r
- id="Background">\r
- <stop\r
- id="stop4178"\r
- offset="0"\r
- style="stop-color:#b8b8b8;stop-opacity:1" />\r
- <stop\r
- id="stop4180"\r
- offset="1"\r
- style="stop-color:#c9c9c9;stop-opacity:1" />\r
- </linearGradient>\r
- <filter\r
- style="color-interpolation-filters:sRGB;"\r
- inkscape:label="Inner Shadow"\r
- id="filter1121">\r
- <feFlood\r
- flood-opacity="0.59999999999999998"\r
- flood-color="rgb(0,0,0)"\r
- result="flood"\r
- id="feFlood1123" />\r
- <feComposite\r
- in="flood"\r
- in2="SourceGraphic"\r
- operator="out"\r
- result="composite1"\r
- id="feComposite1125" />\r
- <feGaussianBlur\r
- in="composite1"\r
- stdDeviation="1"\r
- result="blur"\r
- id="feGaussianBlur1127" />\r
- <feOffset\r
- dx="0"\r
- dy="2"\r
- result="offset"\r
- id="feOffset1129" />\r
- <feComposite\r
- in="offset"\r
- in2="SourceGraphic"\r
- operator="atop"\r
- result="composite2"\r
- id="feComposite1131" />\r
- </filter>\r
- <filter\r
- style="color-interpolation-filters:sRGB;"\r
- inkscape:label="Drop Shadow"\r
- id="filter950">\r
- <feFlood\r
- flood-opacity="0.25"\r
- flood-color="rgb(0,0,0)"\r
- result="flood"\r
- id="feFlood952" />\r
- <feComposite\r
- in="flood"\r
- in2="SourceGraphic"\r
- operator="in"\r
- result="composite1"\r
- id="feComposite954" />\r
- <feGaussianBlur\r
- in="composite1"\r
- stdDeviation="1"\r
- result="blur"\r
- id="feGaussianBlur956" />\r
- <feOffset\r
- dx="0"\r
- dy="1"\r
- result="offset"\r
- id="feOffset958" />\r
- <feComposite\r
- in="SourceGraphic"\r
- in2="offset"\r
- operator="over"\r
- result="composite2"\r
- id="feComposite960" />\r
- </filter>\r
- <clipPath\r
- clipPathUnits="userSpaceOnUse"\r
- id="clipPath873">\r
- <g\r
- transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"\r
- id="g875"\r
- inkscape:label="Layer 1"\r
- style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">\r
- <path\r
- style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"\r
- d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"\r
- id="path877"\r
- inkscape:connector-curvature="0"\r
- sodipodi:nodetypes="sssssssss" />\r
- </g>\r
- </clipPath>\r
- <filter\r
- inkscape:collect="always"\r
- id="filter891"\r
- inkscape:label="Badge Shadow">\r
- <feGaussianBlur\r
- inkscape:collect="always"\r
- stdDeviation="0.71999962"\r
- id="feGaussianBlur893" />\r
- </filter>\r
- </defs>\r
- <sodipodi:namedview\r
- id="base"\r
- pagecolor="#ffffff"\r
- bordercolor="#666666"\r
- borderopacity="1.0"\r
- inkscape:pageopacity="0.0"\r
- inkscape:pageshadow="2"\r
- inkscape:zoom="4.0745362"\r
- inkscape:cx="18.514671"\r
- inkscape:cy="49.018169"\r
- inkscape:document-units="px"\r
- inkscape:current-layer="layer1"\r
- showgrid="true"\r
- fit-margin-top="0"\r
- fit-margin-left="0"\r
- fit-margin-right="0"\r
- fit-margin-bottom="0"\r
- inkscape:window-width="1920"\r
- inkscape:window-height="1029"\r
- inkscape:window-x="0"\r
- inkscape:window-y="24"\r
- inkscape:window-maximized="1"\r
- showborder="true"\r
- showguides="true"\r
- inkscape:guide-bbox="true"\r
- inkscape:showpageshadow="false">\r
- <inkscape:grid\r
- type="xygrid"\r
- id="grid821" />\r
- <sodipodi:guide\r
- orientation="1,0"\r
- position="16,48"\r
- id="guide823" />\r
- <sodipodi:guide\r
- orientation="0,1"\r
- position="64,80"\r
- id="guide825" />\r
- <sodipodi:guide\r
- orientation="1,0"\r
- position="80,40"\r
- id="guide827" />\r
- <sodipodi:guide\r
- orientation="0,1"\r
- position="64,16"\r
- id="guide829" />\r
- </sodipodi:namedview>\r
- <metadata\r
- id="metadata6522">\r
- <rdf:RDF>\r
- <cc:Work\r
- rdf:about="">\r
- <dc:format>image/svg+xml</dc:format>\r
- <dc:type\r
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />\r
- <dc:title></dc:title>\r
- </cc:Work>\r
- </rdf:RDF>\r
- </metadata>\r
- <g\r
- inkscape:label="BACKGROUND"\r
- inkscape:groupmode="layer"\r
- id="layer1"\r
- transform="translate(268,-635.29076)"\r
- style="display:inline">\r
- <path\r
- style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"\r
- d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"\r
- id="path6455"\r
- inkscape:connector-curvature="0"\r
- sodipodi:nodetypes="sssssssss" />\r
- </g>\r
- <g\r
- inkscape:groupmode="layer"\r
- id="layer3"\r
- inkscape:label="PLACE YOUR PICTOGRAM HERE"\r
- style="display:inline" />\r
- <g\r
- inkscape:groupmode="layer"\r
- id="layer2"\r
- inkscape:label="BADGE"\r
- style="display:none"\r
- sodipodi:insensitive="true">\r
- <g\r
- style="display:inline"\r
- transform="translate(-340.00001,-581)"\r
- id="g4394"\r
- clip-path="none">\r
- <g\r
- id="g855">\r
- <g\r
- inkscape:groupmode="maskhelper"\r
- id="g870"\r
- clip-path="url(#clipPath873)"\r
- style="opacity:0.6;filter:url(#filter891)">\r
- <path\r
- transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"\r
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"\r
- sodipodi:ry="12"\r
- sodipodi:rx="12"\r
- sodipodi:cy="552.36218"\r
- sodipodi:cx="252"\r
- id="path844"\r
- style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- sodipodi:type="arc" />\r
- </g>\r
- <g\r
- id="g862">\r
- <path\r
- sodipodi:type="arc"\r
- style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- id="path4398"\r
- sodipodi:cx="252"\r
- sodipodi:cy="552.36218"\r
- sodipodi:rx="12"\r
- sodipodi:ry="12"\r
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"\r
- transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />\r
- <path\r
- transform="matrix(1.25,0,0,1.25,33,-100.45273)"\r
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"\r
- sodipodi:ry="12"\r
- sodipodi:rx="12"\r
- sodipodi:cy="552.36218"\r
- sodipodi:cx="252"\r
- id="path4400"\r
- style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- sodipodi:type="arc" />\r
- <path\r
- sodipodi:type="star"\r
- style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- id="path4459"\r
- sodipodi:sides="5"\r
- sodipodi:cx="666.19574"\r
- sodipodi:cy="589.50385"\r
- sodipodi:r1="7.2431178"\r
- sodipodi:r2="4.3458705"\r
- sodipodi:arg1="1.0471976"\r
- sodipodi:arg2="1.6755161"\r
- inkscape:flatsided="false"\r
- inkscape:rounded="0.1"\r
- inkscape:randomized="0"\r
- d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 C 669.9821,591.68426 670.20862,595.55064 669.8173,595.77657 Z"\r
- transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />\r
- </g>\r
- </g>\r
- </g>\r
- </g>\r
-</svg>\r
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-includes: ['layer:basic', 'layer:vnfproxy']
-options:
- basic:
- use_venv: false
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-
-# 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.
-
-name: broken
-summary: A (broken) simple VNF proxy charm
-maintainer: Adam Israel <adam.israel@canonical.com>
-subordinate: false
-series: ['xenial']
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-metrics:
- uptime:
- type: gauge
- description: "Uptime of the VNF"
- command: awk '{print $1}' /proc/uptime
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from charmhelpers.core.hookenv import (
- action_get,
- action_fail,
- action_set,
- status_set,
-)
-from charms.reactive import (
- clear_flag,
- set_flag,
- when,
- when_not,
-)
-import charms.sshproxy
-
-
-@when('sshproxy.configured')
-@when_not('simple.installed')
-def install_simple_proxy_charm():
- """Post-install actions.
-
- This function will run when two conditions are met:
- 1. The 'sshproxy.configured' state is set
- 2. The 'simple.installed' state is not set
-
- This ensures that the workload status is set to active only when the SSH
- proxy is properly configured.
- """
- set_flag('simple.installed')
- status_set('active', 'Ready!')
-
-
-@when('actions.touch')
-def touch():
- raise Exception("I am broken.")
- err = ''
- try:
- filename = action_get('filename')
- cmd = ['touch {}'.format(filename)]
- result, err = charms.sshproxy._run(cmd)
- except Exception:
- action_fail('command failed:' + err)
- else:
- action_set({'output': result})
- finally:
- clear_flag('actions.touch')
+++ /dev/null
-# Overview
-
-Metrics collection via machine charm
\ No newline at end of file
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-options:
- string-option:
- type: string
- default: "Default Value"
- description: "A short description of the configuration option"
- boolean-option:
- type: boolean
- default: False
- description: "A short description of the configuration option"
- int-option:
- type: int
- default: 9001
- description: "A short description of the configuration option"
-
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-includes: ['layer:basic', 'layer:metrics'] # if you use any interfaces, add them here
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-name: metrics-ci
-summary: <Fill in summary here>
-maintainer: Adam Israel <Adam.Israel@ronin>
-description: |
- <Multi-line description here>
-tags:
- # Replace "misc" with one or more whitelisted tags from this list:
- # https://jujucharms.com/docs/stable/authors-charm-metadata
- - misc
-subordinate: false
-series:
- - xenial
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-metrics:
- users:
- type: gauge
- description: "# of users"
- command: who|wc -l
- load:
- type: gauge
- description: "5 minute load average"
- command: cat /proc/loadavg |awk '{print $1}'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from charmhelpers.core.hookenv import (
- status_set,
-)
-from charms.reactive import (
- set_flag,
- when_not,
-)
-
-
-@when_not('metrics-ci.installed')
-def install_metrics_ci():
- status_set('active', "Ready!")
- set_flag('metrics-ci.installed')
+++ /dev/null
-# Overview
-
-Test charm for metrics collection via proxy.
\ No newline at end of file
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-options:
- string-option:
- type: string
- default: "Default Value"
- description: "A short description of the configuration option"
- boolean-option:
- type: boolean
- default: False
- description: "A short description of the configuration option"
- int-option:
- type: int
- default: 9001
- description: "A short description of the configuration option"
-
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-includes:
- - 'layer:basic'
- - 'layer:vnfproxy'
- - 'layer:sshproxy'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-name: metrics-proxy-ci
-summary: <Fill in summary here>
-maintainer: Adam Israel <Adam.Israel@ronin>
-description: |
- <Multi-line description here>
-tags:
- # Replace "misc" with one or more whitelisted tags from this list:
- # https://jujucharms.com/docs/stable/authors-charm-metadata
- - misc
-subordinate: false
-series:
- - xenial
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-metrics:
- users:
- type: gauge
- description: "# of users"
- command: who|wc -l
- load:
- type: gauge
- description: "5 minute load average"
- command: cat /proc/loadavg |awk '{print $1}'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from charmhelpers.core.hookenv import (
- status_set,
-)
-from charms.reactive import (
- set_flag,
- when_not,
-)
-
-
-@when_not('metrics-ci.installed')
-def install_metrics_ci():
- status_set('blocked', "Waiting for SSH credentials.")
- set_flag('metrics-ci.installed')
+++ /dev/null
-# Overview
-
-A native charm.
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-test:
- description: "Verify that the action can run."
-testint:
- description: "Test a primitive with a non-string parameter"
- params:
- intval:
- type: integer
- default: 0
+++ /dev/null
-#!/usr/bin/env python3
-##
-# Copyright 2016 Canonical Ltd.
-# All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-##
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main, set_flag
-from charmhelpers.core.hookenv import action_fail, action_name
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_flag('actions.{}'.format(action_name()))
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-#!/usr/bin/env python3
-##
-# Copyright 2016 Canonical Ltd.
-# All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-##
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main, set_flag
-from charmhelpers.core.hookenv import action_fail, action_name
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_flag('actions.{}'.format(action_name()))
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-includes:
- - 'layer:basic'
- - 'interface:mysql'
-
-options:
- basic:
- use_venv: false
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-name: native-ci
-summary: A native VNF charm
-description: A native VNF charm
-maintainer: Adam Israel <adam.israel@canonical.com>
-subordinate: false
-series: ['xenial']
-# provides:
-# db:
-# interface: mysql
-# requires:
-# app:
-# interface: mysql
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from charmhelpers.core.hookenv import (
- action_fail,
- action_set,
- action_get,
- status_set,
-)
-from charms.reactive import (
- clear_flag,
- set_flag,
- when,
- when_not,
-)
-
-
-@when_not('native-ci.installed')
-def install_native_ci_charm():
- set_flag('native-ci.installed')
- status_set('active', 'Ready!')
-
-
-@when('actions.test', 'native-ci.installed')
-def test():
- try:
- result = True
- except Exception as e:
- action_fail('command failed: {}'.format(e))
- else:
- action_set({'output': result})
- finally:
- clear_flag('actions.test')
-
-
-@when('actions.testint', 'native-ci.installed')
-def testint():
- try:
- # Test the value is an int by performing a mathmatical operation on it.
- intval = action_get('intval')
- intval = intval + 1
- except Exception as e:
- action_fail('command failed: {}'.format(e))
- else:
- action_set({'output': intval})
- finally:
- clear_flag('actions.testint')
-
-
-# @when('db.joined')
-# def provides_db(db):
-# """Simulate providing database credentials."""
-# db.configure(
-# database="mydb",
-# user="myuser",
-# password="mypassword",
-# host="myhost",
-# slave="myslave",
-# )
-
-
-# @when('db.available')
-# def requires_db(db):
-# """Simulate receiving database credentials."""
-# pass
+++ /dev/null
-# Overview
-
-A `charm layer` to test the functionality of proxy charms.
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-test:
- description: "Verify that the action can run."
+++ /dev/null
-#!/usr/bin/env python3
-##
-# Copyright 2016 Canonical Ltd.
-# All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-##
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main, set_flag
-from charmhelpers.core.hookenv import action_fail, action_name
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_flag('actions.{}'.format(action_name()))
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-includes:
- - 'layer:basic'
- - 'layer:vnfproxy'
- - 'layer:sshproxy'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-name: proxy-ci
-summary: <Fill in summary here>
-maintainer: Adam Israel <Adam.Israel@ronin>
-description: |
- <Multi-line description here>
-tags:
- # Replace "misc" with one or more whitelisted tags from this list:
- # https://jujucharms.com/docs/stable/authors-charm-metadata
- - misc
-subordinate: false
-series:
- - xenial
-provides:
- db:
- interface: mysql
-requires:
- app:
- interface: mysql
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from charmhelpers.core.hookenv import (
- action_fail,
- action_set,
- status_set,
-)
-from charms.reactive import (
- set_flag,
- clear_flag,
- when_not,
- when,
-)
-import charms.sshproxy
-
-
-@when_not('proxy-ci.installed')
-def install_proxy_ci():
- status_set('blocked', "Waiting for SSH credentials.")
- set_flag('proxy-ci.installed')
-
-
-@when('actions.test', 'proxy-ci.installed')
-def test():
- err = ''
- try:
- cmd = ['hostname']
- result, err = charms.sshproxy._run(cmd)
- if len(result) == 0:
- raise Exception("Proxy failed")
- except Exception as e:
- action_fail('command failed: {}'.format(e))
- else:
- action_set({'output': result})
- finally:
- clear_flag('actions.test')
-
-
-@when('db.joined')
-def provides_db(db):
- """Simulate providing database credentials."""
- db.configure(
- database="mydb",
- user="myuser",
- password="mypassword",
- host="myhost",
- slave="myslave",
- )
-
-
-@when('db.available')
-def requires_db(db):
- """Simulate receiving database credentials."""
- pass
+++ /dev/null
-# Overview
-
-A simple charm
\ No newline at end of file
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-touch:
- description: "Touch a file on the VNF."
- params:
- filename:
- description: "The name of the file to touch."
- type: string
- default: ""
- required:
- - filename
+++ /dev/null
-#!/usr/bin/env python3
-##
-# Copyright 2016 Canonical Ltd.
-# All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-##
-import sys
-sys.path.append('lib')
-
-from charms.reactive import main, set_flag
-from charmhelpers.core.hookenv import action_fail, action_name
-
-"""
-`set_state` only works here because it's flushed to disk inside the `main()`
-loop. remove_state will need to be called inside the action method.
-"""
-set_flag('actions.{}'.format(action_name()))
-
-try:
- main()
-except Exception as e:
- action_fail(repr(e))
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-options:
- string-option:
- type: string
- default: "Default Value"
- description: "A short description of the configuration option"
- boolean-option:
- type: boolean
- default: False
- description: "A short description of the configuration option"
- int-option:
- type: int
- default: 9001
- description: "A short description of the configuration option"
-
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
-<!-- Created with Inkscape (http://www.inkscape.org/) -->\r
-\r
-<svg\r
- xmlns:dc="http://purl.org/dc/elements/1.1/"\r
- xmlns:cc="http://creativecommons.org/ns#"\r
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\r
- xmlns:svg="http://www.w3.org/2000/svg"\r
- xmlns="http://www.w3.org/2000/svg"\r
- xmlns:xlink="http://www.w3.org/1999/xlink"\r
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"\r
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"\r
- width="96"\r
- height="96"\r
- id="svg6517"\r
- version="1.1"\r
- inkscape:version="0.48+devel r12274"\r
- sodipodi:docname="Juju_charm_icon_template.svg">\r
- <defs\r
- id="defs6519">\r
- <linearGradient\r
- inkscape:collect="always"\r
- xlink:href="#Background"\r
- id="linearGradient6461"\r
- gradientUnits="userSpaceOnUse"\r
- x1="0"\r
- y1="970.29498"\r
- x2="144"\r
- y2="970.29498"\r
- gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" />\r
- <linearGradient\r
- id="Background">\r
- <stop\r
- id="stop4178"\r
- offset="0"\r
- style="stop-color:#b8b8b8;stop-opacity:1" />\r
- <stop\r
- id="stop4180"\r
- offset="1"\r
- style="stop-color:#c9c9c9;stop-opacity:1" />\r
- </linearGradient>\r
- <filter\r
- style="color-interpolation-filters:sRGB;"\r
- inkscape:label="Inner Shadow"\r
- id="filter1121">\r
- <feFlood\r
- flood-opacity="0.59999999999999998"\r
- flood-color="rgb(0,0,0)"\r
- result="flood"\r
- id="feFlood1123" />\r
- <feComposite\r
- in="flood"\r
- in2="SourceGraphic"\r
- operator="out"\r
- result="composite1"\r
- id="feComposite1125" />\r
- <feGaussianBlur\r
- in="composite1"\r
- stdDeviation="1"\r
- result="blur"\r
- id="feGaussianBlur1127" />\r
- <feOffset\r
- dx="0"\r
- dy="2"\r
- result="offset"\r
- id="feOffset1129" />\r
- <feComposite\r
- in="offset"\r
- in2="SourceGraphic"\r
- operator="atop"\r
- result="composite2"\r
- id="feComposite1131" />\r
- </filter>\r
- <filter\r
- style="color-interpolation-filters:sRGB;"\r
- inkscape:label="Drop Shadow"\r
- id="filter950">\r
- <feFlood\r
- flood-opacity="0.25"\r
- flood-color="rgb(0,0,0)"\r
- result="flood"\r
- id="feFlood952" />\r
- <feComposite\r
- in="flood"\r
- in2="SourceGraphic"\r
- operator="in"\r
- result="composite1"\r
- id="feComposite954" />\r
- <feGaussianBlur\r
- in="composite1"\r
- stdDeviation="1"\r
- result="blur"\r
- id="feGaussianBlur956" />\r
- <feOffset\r
- dx="0"\r
- dy="1"\r
- result="offset"\r
- id="feOffset958" />\r
- <feComposite\r
- in="SourceGraphic"\r
- in2="offset"\r
- operator="over"\r
- result="composite2"\r
- id="feComposite960" />\r
- </filter>\r
- <clipPath\r
- clipPathUnits="userSpaceOnUse"\r
- id="clipPath873">\r
- <g\r
- transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"\r
- id="g875"\r
- inkscape:label="Layer 1"\r
- style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">\r
- <path\r
- style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"\r
- d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"\r
- id="path877"\r
- inkscape:connector-curvature="0"\r
- sodipodi:nodetypes="sssssssss" />\r
- </g>\r
- </clipPath>\r
- <filter\r
- inkscape:collect="always"\r
- id="filter891"\r
- inkscape:label="Badge Shadow">\r
- <feGaussianBlur\r
- inkscape:collect="always"\r
- stdDeviation="0.71999962"\r
- id="feGaussianBlur893" />\r
- </filter>\r
- </defs>\r
- <sodipodi:namedview\r
- id="base"\r
- pagecolor="#ffffff"\r
- bordercolor="#666666"\r
- borderopacity="1.0"\r
- inkscape:pageopacity="0.0"\r
- inkscape:pageshadow="2"\r
- inkscape:zoom="4.0745362"\r
- inkscape:cx="18.514671"\r
- inkscape:cy="49.018169"\r
- inkscape:document-units="px"\r
- inkscape:current-layer="layer1"\r
- showgrid="true"\r
- fit-margin-top="0"\r
- fit-margin-left="0"\r
- fit-margin-right="0"\r
- fit-margin-bottom="0"\r
- inkscape:window-width="1920"\r
- inkscape:window-height="1029"\r
- inkscape:window-x="0"\r
- inkscape:window-y="24"\r
- inkscape:window-maximized="1"\r
- showborder="true"\r
- showguides="true"\r
- inkscape:guide-bbox="true"\r
- inkscape:showpageshadow="false">\r
- <inkscape:grid\r
- type="xygrid"\r
- id="grid821" />\r
- <sodipodi:guide\r
- orientation="1,0"\r
- position="16,48"\r
- id="guide823" />\r
- <sodipodi:guide\r
- orientation="0,1"\r
- position="64,80"\r
- id="guide825" />\r
- <sodipodi:guide\r
- orientation="1,0"\r
- position="80,40"\r
- id="guide827" />\r
- <sodipodi:guide\r
- orientation="0,1"\r
- position="64,16"\r
- id="guide829" />\r
- </sodipodi:namedview>\r
- <metadata\r
- id="metadata6522">\r
- <rdf:RDF>\r
- <cc:Work\r
- rdf:about="">\r
- <dc:format>image/svg+xml</dc:format>\r
- <dc:type\r
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />\r
- <dc:title></dc:title>\r
- </cc:Work>\r
- </rdf:RDF>\r
- </metadata>\r
- <g\r
- inkscape:label="BACKGROUND"\r
- inkscape:groupmode="layer"\r
- id="layer1"\r
- transform="translate(268,-635.29076)"\r
- style="display:inline">\r
- <path\r
- style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"\r
- d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"\r
- id="path6455"\r
- inkscape:connector-curvature="0"\r
- sodipodi:nodetypes="sssssssss" />\r
- </g>\r
- <g\r
- inkscape:groupmode="layer"\r
- id="layer3"\r
- inkscape:label="PLACE YOUR PICTOGRAM HERE"\r
- style="display:inline" />\r
- <g\r
- inkscape:groupmode="layer"\r
- id="layer2"\r
- inkscape:label="BADGE"\r
- style="display:none"\r
- sodipodi:insensitive="true">\r
- <g\r
- style="display:inline"\r
- transform="translate(-340.00001,-581)"\r
- id="g4394"\r
- clip-path="none">\r
- <g\r
- id="g855">\r
- <g\r
- inkscape:groupmode="maskhelper"\r
- id="g870"\r
- clip-path="url(#clipPath873)"\r
- style="opacity:0.6;filter:url(#filter891)">\r
- <path\r
- transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"\r
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"\r
- sodipodi:ry="12"\r
- sodipodi:rx="12"\r
- sodipodi:cy="552.36218"\r
- sodipodi:cx="252"\r
- id="path844"\r
- style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- sodipodi:type="arc" />\r
- </g>\r
- <g\r
- id="g862">\r
- <path\r
- sodipodi:type="arc"\r
- style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- id="path4398"\r
- sodipodi:cx="252"\r
- sodipodi:cy="552.36218"\r
- sodipodi:rx="12"\r
- sodipodi:ry="12"\r
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"\r
- transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />\r
- <path\r
- transform="matrix(1.25,0,0,1.25,33,-100.45273)"\r
- d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"\r
- sodipodi:ry="12"\r
- sodipodi:rx="12"\r
- sodipodi:cy="552.36218"\r
- sodipodi:cx="252"\r
- id="path4400"\r
- style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- sodipodi:type="arc" />\r
- <path\r
- sodipodi:type="star"\r
- style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"\r
- id="path4459"\r
- sodipodi:sides="5"\r
- sodipodi:cx="666.19574"\r
- sodipodi:cy="589.50385"\r
- sodipodi:r1="7.2431178"\r
- sodipodi:r2="4.3458705"\r
- sodipodi:arg1="1.0471976"\r
- sodipodi:arg2="1.6755161"\r
- inkscape:flatsided="false"\r
- inkscape:rounded="0.1"\r
- inkscape:randomized="0"\r
- d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 C 669.9821,591.68426 670.20862,595.55064 669.8173,595.77657 Z"\r
- transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />\r
- </g>\r
- </g>\r
- </g>\r
- </g>\r
-</svg>\r
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-includes: ['layer:basic', 'layer:vnfproxy']
-options:
- basic:
- use_venv: false
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-name: simple
-summary: A simple VNF proxy charm
-maintainer: Adam Israel <adam.israel@canonical.com>
-subordinate: false
-series: ['xenial']
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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.
-
-metrics:
- uptime:
- type: gauge
- description: "Uptime of the VNF"
- command: awk '{print $1}' /proc/uptime
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from charmhelpers.core.hookenv import (
- action_get,
- action_fail,
- action_set,
- status_set,
-)
-from charms.reactive import (
- clear_flag,
- set_flag,
- when,
- when_not,
-)
-import charms.sshproxy
-import os
-
-
-@when('sshproxy.configured')
-@when_not('simple.installed')
-def install_simple_proxy_charm():
- """Post-install actions.
-
- This function will run when two conditions are met:
- 1. The 'sshproxy.configured' state is set
- 2. The 'simple.installed' state is not set
-
- This ensures that the workload status is set to active only when the SSH
- proxy is properly configured.
- """
- set_flag('simple.installed')
- status_set('active', 'Ready!')
-
-
-@when('actions.touch')
-def touch():
- if not in_action_context():
- clear_flag('actions.touch')
- return
-
- err = ''
- try:
- filename = action_get('filename')
- cmd = ['touch {}'.format(filename)]
- result, err = charms.sshproxy._run(cmd)
- except Exception:
- action_fail('command failed:' + err)
- else:
- action_set({'output': result})
- finally:
- clear_flag('actions.touch')
-
-
-def in_action_context():
- """Determine whether we're running on an action context."""
- return 'JUJU_ACTION_UUID' in os.environ
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Test a charm that breaks post-deployment
-"""
-
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: brokencharm-ns
- name: brokencharm-ns
- short-name: brokencharm-ns
- description: NS with 1 VNF connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: hackfest-simplecharm-vnf
- name: hackfest-simplecharm-vnf
- short-name: hackfest-simplecharm-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: hackfest3-mgmt
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: PARAVIRT
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: PARAVIRT
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- - id: dataVM
- name: dataVM
- image: hackfest3-mgmt
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: dataVM-eth0
- position: '1'
- type: INTERNAL
- virtual-interface:
- type: PARAVIRT
- internal-connection-point-ref: dataVM-internal
- - name: dataVM-xe0
- position: '2'
- type: EXTERNAL
- virtual-interface:
- type: PARAVIRT
- external-connection-point-ref: vnf-data
- internal-connection-point:
- - id: dataVM-internal
- name: dataVM-internal
- short-name: dataVM-internal
- type: VPORT
- vnf-configuration:
- juju:
- charm: broken
- proxy: true
- initial-config-primitive:
- - seq: '1'
- name: touch
- parameter:
- - name: filename
- value: '/home/ubuntu/first-touch'
- config-primitive:
- - name: touch
- parameter:
- - name: filename
- data-type: STRING
- default-value: '/home/ubuntu/touched'
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_charm_proxy(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
- logging.debug("test_charm_proxy stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a native charm (to LXD) and execute a primitive
-"""
-
-import asyncio
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: charmnative-ns
- name: charmnative-ns
- short-name: charmnative-ns
- description: NS with 1 VNFs charmnative-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: charmnative-vnf
- name: charmnative-vnf
- short-name: charmnative-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: native-ci
- proxy: false
- initial-config-primitive:
- - seq: '1'
- name: test
- """
-
- @pytest.mark.asyncio
- async def test_charm_native(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- loop=event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
-
- print("test_charm_native stopped")
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a VNF with a proxy charm, executing an initial-config-primitive
-"""
-
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: charmproxy-ns
- name: charmproxy-ns
- short-name: charmproxy-ns
- description: NS with 1 VNF connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: charmproxy-vnf
- name: charmproxy-vnf
- short-name: charmproxy-vnf
- version: '1.0'
- description: A VNF consisting of 1 VDUs w/proxy charm
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: proxy-ci
- proxy: true
- initial-config-primitive:
- - seq: '1'
- name: test
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_charm_proxy(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
- logging.debug("test_charm_proxy stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a multi-vdu, multi-charm VNF
-"""
-
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: multivdurelate-ns
- name: multivdurelate-ns
- short-name: multivdurelate-ns
- description: NS with 1 VNF connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: multivdurelate-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: multivdurelate-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: multivdurelate-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: multivdurelate-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: multivdurelate-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: multivdurelate-vnf
- name: multivdurelate-vnf
- short-name: multivdurelate-vnf
- version: '1.0'
- description: A VNF consisting of 1 VDUs w/proxy charm
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: proxy-ci
- proxy: true
- vca-relationships:
- # Relation needs to map to the vdu providing or
- # requiring, so that we can map to the deployed app.
- relation:
- - provides: dataVM:db
- requires: mgmtVM:app
- initial-config-primitive:
- - seq: '1'
- name: test
- - id: dataVM
- name: dataVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: dataVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: dataVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: dataVM-internal
- internal-connection-point:
- - id: dataVM-internal
- name: dataVM-internal
- short-name: dataVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: proxy-ci
- proxy: true
- initial-config-primitive:
- - seq: '1'
- name: test
-
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_multivdu_relate(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
- vnf_index += 1
-
- while await self.running():
- logging.debug("Waiting for test to finish...")
- await asyncio.sleep(15)
-
- # assert False
- logging.debug("test_multivdu_relate stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a multi-vdu, multi-charm VNF
-"""
-
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
-nsd:nsd-catalog:
- nsd:
- - id: hackfest-simplecharm-ns
- name: hackfest-simplecharm-ns
- short-name: hackfest-simplecharm-ns
- description: NS with 2 VNFs hackfest-simplecharm-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: hackfest-simplecharm-vnf
- member-vnf-index: '1'
- - vnfd-id-ref: hackfest-simplecharm-vnf
- member-vnf-index: '2'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: hackfest-simplecharm-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: hackfest-simplecharm-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: hackfest-simplecharm-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: hackfest-simplecharm-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
-vnfd:vnfd-catalog:
- vnfd:
- - id: hackfest-simplecharm-vnf
- name: hackfest-simplecharm-vnf
- short-name: hackfest-simplecharm-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: hackfest3-mgmt
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: PARAVIRT
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: PARAVIRT
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- - id: dataVM
- name: dataVM
- image: hackfest3-mgmt
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: dataVM-eth0
- position: '1'
- type: INTERNAL
- virtual-interface:
- type: PARAVIRT
- internal-connection-point-ref: dataVM-internal
- - name: dataVM-xe0
- position: '2'
- type: EXTERNAL
- virtual-interface:
- type: PARAVIRT
- external-connection-point-ref: vnf-data
- internal-connection-point:
- - id: dataVM-internal
- name: dataVM-internal
- short-name: dataVM-internal
- type: VPORT
- vnf-configuration:
- juju:
- charm: simple
- initial-config-primitive:
- - seq: '1'
- name: config
- parameter:
- - name: ssh-hostname
- value: <rw_mgmt_ip>
- - name: ssh-username
- value: ubuntu
- - name: ssh-password
- value: osm4u
- - seq: '2'
- name: touch
- parameter:
- - name: filename
- value: '/home/ubuntu/first-touch'
- config-primitive:
- - name: touch
- parameter:
- - name: filename
- data-type: STRING
- default-value: '/home/ubuntu/touched'
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_multivdu_multicharm(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
- vnf_index += 1
-
- while await self.running():
- logging.debug("Waiting for test to finish...")
- await asyncio.sleep(15)
- # assert False
- logging.debug("test_multivdu_multicharm stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a VNF w/native charm that collects metrics
-"""
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: metricsnative-ns
- name: metricsnative-ns
- short-name: metricsnative-ns
- description: NS with 1 VNFs metricsnative-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: metricsnative-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: metricsnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: metricsnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: metricsnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: metricsnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: metricsnative-vnf
- name: metricsnative-vnf
- short-name: metricsnative-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vnf-configuration:
- juju:
- charm: metrics-ci
- proxy: false
- config-primitive:
- - name: touch
- parameter:
- - name: filename
- data-type: STRING
- default-value: '/home/ubuntu/touched'
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_metrics_native(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
-
- logging.debug("test_metrics_native stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a VNF w/proxy charm that collects metrics
-"""
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: metricsproxy-ns
- name: metricsproxy-ns
- short-name: metricsproxy-ns
- description: NS with 1 VNFs metricsproxy-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: metricsproxy-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: metricsproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: metricsproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: metricsproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: metricsproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: metricsproxy-vnf
- name: metricsproxy-vnf
- short-name: metricsproxy-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vnf-configuration:
- juju:
- charm: metrics-proxy-ci
- proxy: true
- initial-config-primitive:
- - seq: '1'
- name: run
- parameter:
- - name: command
- data-type: STRING
- value: hostname
-"""
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_metrics_proxy(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
-
- logging.debug("test_metrics_proxy stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a multi-vdu, multi-charm VNF
-"""
-
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: multivdumulticharm-ns
- name: multivdumulticharm-ns
- short-name: multivdumulticharm-ns
- description: NS with 1 VNF connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: multivdumulticharm-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: multivdumulticharm-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: multivdumulticharm-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: multivdumulticharm-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: multivdumulticharm-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: multivdumulticharm-vnf
- name: multivdumulticharm-vnf
- short-name: multivdumulticharm-vnf
- version: '1.0'
- description: A VNF consisting of 1 VDUs w/proxy charm
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: proxy-ci
- proxy: true
- initial-config-primitive:
- - seq: '1'
- name: test
- - id: dataVM
- name: dataVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: dataVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: dataVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: dataVM-internal
- internal-connection-point:
- - id: dataVM-internal
- name: dataVM-internal
- short-name: dataVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: proxy-ci
- proxy: true
- # Relation needs to map to the vdu providing or
- # requiring, so that we can map to the deployed app.
- relation:
- - provides: dataVM:db
- requires: mgmtVM:app
- initial-config-primitive:
- - seq: '1'
- name: test
-
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_multivdu_multicharm(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
- vnf_index += 1
-
- while await self.running():
- logging.debug("Waiting for test to finish...")
- await asyncio.sleep(15)
- # assert False
- logging.debug("test_multivdu_multicharm stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Test N2VC when the VNF descriptor does not contain an initial-config-primitive.
-"""
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: noinitconfig-ns
- name: noinitconfig-ns
- short-name: noinitconfig-ns
- description: NS with 1 VNFs noinitconfig-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: noinitconfig-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: noinitconfig-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: noinitconfig-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: noinitconfig-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: noinitconfig-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: noinitconfig-vnf
- name: noinitconfig-vnf
- short-name: noinitconfig-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: native-ci
- proxy: false
- config-primitive:
- - name: test
-
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_charm_no_initial_config_primitive(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
-
- logging.debug("test_charm_no_initial_config_primitive stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Describe what this test is meant to do.
-"""
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: noparam-ns
- name: noparam-ns
- short-name: noparam-ns
- description: NS with 1 VNFs noparam-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: noparam-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: noparam-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: noparam-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: noparam-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: noparam-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: noparam-vnf
- name: noparam-vnf
- short-name: noparam-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: native-ci
- proxy: false
- initial-config-primitive:
- - seq: '1'
- name: test
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_charm_no_parameter(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
- logging.warning("event_loop: {}".format(event_loop))
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a VNF and execute a non-existent primitive
-"""
-import asyncio
-import logging
-import pytest
-from .. import base
-# import n2vc.vnf
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: nonexistent-ns
- name: nonexistent-ns
- short-name: nonexistent-ns
- description: NS with 1 VNFs charmnative-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: charmnative-vnf
- name: charmnative-vnf
- short-name: charmnative-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: native-ci
- proxy: false
- initial-config-primitive:
- - seq: '1'
- name: idonotexist
- - seq: '2'
- name: test
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_charm_non_existent_primitive(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
- logging.debug("test_charm_non_string_parameter stopped")
-
-
- # assert False
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Deploy a VNF with a non-string parameter passed to a primitive
-"""
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: nonstring-ns
- name: nonstring-ns
- short-name: nonstring-ns
- description: NS with 1 VNFs charmnative-vnf connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: charmnative-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: charmnative-vnf
- name: charmnative-vnf
- short-name: charmnative-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: xenial
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: VIRTIO
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: VIRTIO
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- vdu-configuration:
- juju:
- charm: native-ci
- proxy: false
- initial-config-primitive:
- - seq: '1'
- name: test
- - seq: '2'
- name: testint
- parameter:
- - name: intval
- data-type: INTEGER
- value: 1
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_charm_non_string_parameter(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
- logging.debug("test_charm_non_string_parameter stopped")
- # assert False
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# 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
-#
-
-"""
-Exercise the simplecharm hackfest example:
-https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/hackfest_simplecharm_vnf.tar.gz
-"""
-
-import asyncio
-import logging
-import pytest
-from .. import base
-
-
-# @pytest.mark.serial
-class TestCharm(base.TestN2VC):
-
- NSD_YAML = """
- nsd:nsd-catalog:
- nsd:
- - id: charmproxy-ns
- name: charmproxy-ns
- short-name: charmproxy-ns
- description: NS with 1 VNF connected by datanet and mgmtnet VLs
- version: '1.0'
- logo: osm.png
- constituent-vnfd:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index: '1'
- vld:
- - id: mgmtnet
- name: mgmtnet
- short-name: mgmtnet
- type: ELAN
- mgmt-network: 'true'
- vim-network-name: mgmt
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-mgmt
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-mgmt
- - id: datanet
- name: datanet
- short-name: datanet
- type: ELAN
- vnfd-connection-point-ref:
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '1'
- vnfd-connection-point-ref: vnf-data
- - vnfd-id-ref: charmproxy-vnf
- member-vnf-index-ref: '2'
- vnfd-connection-point-ref: vnf-data
- """
-
- VNFD_YAML = """
- vnfd:vnfd-catalog:
- vnfd:
- - id: hackfest-simplecharm-vnf
- name: hackfest-simplecharm-vnf
- short-name: hackfest-simplecharm-vnf
- version: '1.0'
- description: A VNF consisting of 2 VDUs connected to an internal VL, and one VDU with cloud-init
- logo: osm.png
- connection-point:
- - id: vnf-mgmt
- name: vnf-mgmt
- short-name: vnf-mgmt
- type: VPORT
- - id: vnf-data
- name: vnf-data
- short-name: vnf-data
- type: VPORT
- mgmt-interface:
- cp: vnf-mgmt
- internal-vld:
- - id: internal
- name: internal
- short-name: internal
- type: ELAN
- internal-connection-point:
- - id-ref: mgmtVM-internal
- - id-ref: dataVM-internal
- vdu:
- - id: mgmtVM
- name: mgmtVM
- image: hackfest3-mgmt
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: mgmtVM-eth0
- position: '1'
- type: EXTERNAL
- virtual-interface:
- type: PARAVIRT
- external-connection-point-ref: vnf-mgmt
- - name: mgmtVM-eth1
- position: '2'
- type: INTERNAL
- virtual-interface:
- type: PARAVIRT
- internal-connection-point-ref: mgmtVM-internal
- internal-connection-point:
- - id: mgmtVM-internal
- name: mgmtVM-internal
- short-name: mgmtVM-internal
- type: VPORT
- cloud-init-file: cloud-config.txt
- - id: dataVM
- name: dataVM
- image: hackfest3-mgmt
- count: '1'
- vm-flavor:
- vcpu-count: '1'
- memory-mb: '1024'
- storage-gb: '10'
- interface:
- - name: dataVM-eth0
- position: '1'
- type: INTERNAL
- virtual-interface:
- type: PARAVIRT
- internal-connection-point-ref: dataVM-internal
- - name: dataVM-xe0
- position: '2'
- type: EXTERNAL
- virtual-interface:
- type: PARAVIRT
- external-connection-point-ref: vnf-data
- internal-connection-point:
- - id: dataVM-internal
- name: dataVM-internal
- short-name: dataVM-internal
- type: VPORT
- vnf-configuration:
- juju:
- charm: simple
- proxy: true
- initial-config-primitive:
- - seq: '1'
- name: touch
- parameter:
- - name: filename
- value: '/home/ubuntu/first-touch'
- config-primitive:
- - name: touch
- parameter:
- - name: filename
- data-type: STRING
- default-value: '/home/ubuntu/touched'
- """
-
- # @pytest.mark.serial
- @pytest.mark.asyncio
- async def test_charm_proxy(self, event_loop):
- """Deploy and execute the initial-config-primitive of a VNF."""
-
- if self.nsd and self.vnfd:
- vnf_index = 0
-
- for config in self.get_config():
- juju = config['juju']
- charm = juju['charm']
-
- await self.deploy(
- vnf_index,
- charm,
- config,
- event_loop,
- )
-
- while await self.running():
- print("Waiting for test to finish...")
- await asyncio.sleep(15)
- logging.debug("test_charm_proxy stopped")
-
- return 'ok'
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import argparse
-import asyncio
-import logging
-import n2vc.k8s_juju_conn
-from base import get_juju_public_key
-import os
-from osm_common.fslocal import FsLocal
-import subprocess
-import yaml
-
-
-def get_args():
- parser = argparse.ArgumentParser()
- parser.add_argument("--cluster_uuid", help='The UUID of an existing cluster to use', default=None)
- parser.add_argument("--reset", action="store_true")
- return parser.parse_args()
-
-async def main():
-
- args = get_args()
-
- reuse_cluster_uuid = args.cluster_uuid
-
- log = logging.getLogger()
- log.level = logging.DEBUG
-
- # Extract parameters from the environment in order to run our tests
- vca_host = os.getenv('VCA_HOST', '127.0.0.1')
- vca_port = os.getenv('VCA_PORT', 17070)
- vca_user = os.getenv('VCA_USER', 'admin')
- vca_charms = os.getenv('VCA_CHARMS', None)
- vca_secret = os.getenv('VCA_SECRET', None)
- vca_ca_cert = os.getenv('VCA_CACERT', None)
-
- # Get the Juju Public key
- juju_public_key = get_juju_public_key()
- if juju_public_key:
- with open(juju_public_key, 'r') as f:
- juju_public_key = f.read()
- else:
- raise Exception("No Juju Public Key found")
-
- storage = {
- 'driver': 'local',
- 'path': '/srv/app/storage'
- }
- fs = FsLocal()
- fs.fs_connect(storage)
-
- client = n2vc.k8s_juju_conn.K8sJujuConnector(
- kubectl_command='/snap/bin/kubectl',
- juju_command='/snap/bin/juju',
- fs=fs,
- db=None,
- )
-
- # kubectl config view --raw
- # microk8s.config
-
- # if microk8s then
- # kubecfg = subprocess.getoutput('microk8s.config')
- # else
- kubecfg = subprocess.getoutput('kubectl config view --raw')
- # print(kubecfg)
-
- # k8screds = yaml.load(kubecfg, Loader=yaml.FullLoader)
- namespace = 'testing'
- kdu_model = "./tests/bundles/k8s-zookeeper.yaml"
-
- """init_env"""
- cluster_uuid, _ = await client.init_env(kubecfg, namespace, reuse_cluster_uuid=reuse_cluster_uuid)
- print(cluster_uuid)
-
- if not reuse_cluster_uuid:
- # This is a new cluster, so install to it
-
- """install"""
- # async def install(self, cluster_uuid, kdu_model, atomic=True, timeout=None, params=None):
- # TODO: Re-add storage declaration to bundle. The API doesn't support the storage option yet. David is investigating.
-
- # Deploy the bundle
- kdu_instance = await client.install(cluster_uuid, kdu_model, atomic=True, timeout=600)
-
- if kdu_instance:
- # Inspect
- print("Getting status")
- status = await client.status_kdu(cluster_uuid, kdu_instance)
- print(status)
-
- # Inspect the bundle
- config = await client.inspect_kdu(kdu_model)
- print(config)
-
- readme = await client.help_kdu(kdu_model)
- # print(readme)
-
-
- """upgrade
- Upgrade to a newer version of the bundle
- """
- kdu_model_upgrade = "./tests/bundles/k8s-zookeeper-upgrade.yaml"
- upgraded = await client.upgrade(cluster_uuid, namespace, kdu_model=kdu_model_upgrade)
-
- kdu_model_upgrade = "./tests/bundles/k8s-zookeeper-downgrade.yaml"
- upgraded = await client.upgrade(cluster_uuid, namespace, kdu_model=kdu_model_upgrade)
-
- """uninstall"""
-
- """reset"""
- if args.reset:
- await client.reset(cluster_uuid)
-
- await client.logout()
-
- print("Done")
-
-if __name__ == "__main__":
- asyncio.run(main())
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import base64
-import juju
-import logging
-import n2vc.exceptions
-from n2vc.vnf import N2VC # noqa: F401
-import os
-import pytest
-import re
-import ssl
-import sys
-
-MODEL_NAME = '5e4e7cb0-5678-4b82-97da-9e4a1b51f5d5'
-
-class TestN2VC(object):
-
- @classmethod
- def setup_class(self):
- """ setup any state specific to the execution of the given class (which
- usually contains tests).
- """
- # Initialize instance variable(s)
- self.log = logging.getLogger()
- self.log.level = logging.DEBUG
-
- @classmethod
- def teardown_class(self):
- """ teardown any state that was previously setup with a call to
- setup_class.
- """
- pass
-
- """Utility functions"""
- def get_n2vc(self, params={}):
- """Return an instance of N2VC.VNF."""
-
-
- # Extract parameters from the environment in order to run our test
- vca_host = params['VCA_HOST']
- vca_port = params['VCA_PORT']
- vca_user = params['VCA_USER']
- vca_charms = params['VCA_CHARMS']
- vca_secret = params['VCA_SECRET']
- vca_cacert = params['VCA_CACERT']
- vca_public_key = params['VCA_PUBLIC_KEY']
-
- client = n2vc.vnf.N2VC(
- log=self.log,
- server=vca_host,
- port=vca_port,
- user=vca_user,
- secret=vca_secret,
- artifacts=vca_charms,
- juju_public_key=vca_public_key,
- ca_cert=vca_cacert,
- )
- return client
-
- """Tests"""
-
- def test_vendored_libjuju(self):
- """Test the module import for our vendored version of libjuju.
-
- Test and verify that the version of libjuju being imported by N2VC is our
- vendored version, not one installed externally.
- """
- for name in sys.modules:
- if name.startswith("juju"):
- module = sys.modules[name]
- if getattr(module, "__file__"):
- print(getattr(module, "__file__"))
- assert re.search('n2vc', module.__file__, re.IGNORECASE)
-
- # assert module.__file__.find("N2VC")
- # assert False
- return
-
- @pytest.mark.asyncio
- async def test_connect_invalid_cacert(self):
- params = {
- 'VCA_HOST': os.getenv('VCA_HOST', '127.0.0.1'),
- 'VCA_PORT': os.getenv('VCA_PORT', 17070),
- 'VCA_USER': os.getenv('VCA_USER', 'admin'),
- 'VCA_SECRET': os.getenv('VCA_SECRET', 'admin'),
- 'VCA_CHARMS': os.getenv('VCA_CHARMS', None),
- 'VCA_PUBLIC_KEY': os.getenv('VCA_PUBLIC_KEY', None),
- 'VCA_CACERT': 'invalidcacert',
- }
- with pytest.raises(n2vc.exceptions.InvalidCACertificate):
- client = self.get_n2vc(params)
-
-
- @pytest.mark.asyncio
- async def test_login(self):
- """Test connecting to libjuju."""
- params = {
- 'VCA_HOST': os.getenv('VCA_HOST', '127.0.0.1'),
- 'VCA_PORT': os.getenv('VCA_PORT', 17070),
- 'VCA_USER': os.getenv('VCA_USER', 'admin'),
- 'VCA_SECRET': os.getenv('VCA_SECRET', 'admin'),
- 'VCA_CHARMS': os.getenv('VCA_CHARMS', None),
- 'VCA_PUBLIC_KEY': os.getenv('VCA_PUBLIC_KEY', None),
- 'VCA_CACERT': os.getenv('VCA_CACERT', "invalidcacert"),
- }
-
- client = self.get_n2vc(params)
-
- await client.login()
- assert client.authenticated
-
- await client.logout()
- assert client.authenticated is False
-
- @pytest.mark.asyncio
- async def test_model(self):
- """Test models."""
- params = {
- 'VCA_HOST': os.getenv('VCA_HOST', '127.0.0.1'),
- 'VCA_PORT': os.getenv('VCA_PORT', 17070),
- 'VCA_USER': os.getenv('VCA_USER', 'admin'),
- 'VCA_SECRET': os.getenv('VCA_SECRET', 'admin'),
- 'VCA_CHARMS': os.getenv('VCA_CHARMS', None),
- 'VCA_PUBLIC_KEY': os.getenv('VCA_PUBLIC_KEY', None),
- 'VCA_CACERT': os.getenv('VCA_CACERT', "invalidcacert"),
- }
-
- client = self.get_n2vc(params)
-
- await client.login()
- assert client.authenticated
-
- self.log.debug("Creating model {}".format(MODEL_NAME))
- await client.CreateNetworkService(MODEL_NAME)
-
- # assert that model exists
- model = await client.controller.get_model(MODEL_NAME)
- assert model
-
- await client.DestroyNetworkService(MODEL_NAME)
-
- # Wait for model to be destroyed
- import time
- time.sleep(5)
-
- with pytest.raises(juju.errors.JujuAPIError):
- model = await client.controller.get_model(MODEL_NAME)
-
- await client.logout()
- assert client.authenticated is False
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-
-# 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.
-
-"""
-This test exercises LXD, to make sure that we can:
-1. Create a container profile
-2. Launch a container with a profile
-3. Stop a container
-4. Destroy a container
-5. Delete a container profile
-
-"""
-import logging
-# import os
-import pytest
-from . import base
-import subprocess
-import shlex
-import tempfile
-
-
-@pytest.mark.asyncio
-async def test_lxd():
-
- container = base.create_lxd_container(name="test-lxd")
- assert container is not None
-
- # Get the hostname of the container
- hostname = container.name
-
- # Delete the container
- base.destroy_lxd_container(container)
-
- # Verify the container is deleted
- client = base.get_lxd_client()
- assert client.containers.exists(hostname) is False
-
-
-@pytest.mark.asyncio
-async def test_lxd_ssh():
-
- with tempfile.TemporaryDirectory() as tmp:
- try:
- # Create a temporary keypair
- cmd = shlex.split(
- "ssh-keygen -t rsa -b 4096 -N '' -f {}/id_lxd_rsa".format(
- tmp,
- )
- )
- subprocess.check_call(cmd)
- except subprocess.CalledProcessError as e:
- logging.debug(e)
- assert False
-
- # Slurp the public key
- public_key = None
- with open("{}/id_lxd_rsa.pub".format(tmp), "r") as f:
- public_key = f.read()
-
- assert public_key is not None
-
- # Create the container with the keypair injected via profile
- container = base.create_lxd_container(
- public_key=public_key,
- name="test-lxd"
- )
- assert container is not None
-
- # Get the hostname of the container
- hostname = container.name
-
- addresses = container.state().network['eth0']['addresses']
- # The interface may have more than one address, but we only need
- # the first one for testing purposes.
- ipaddr = addresses[0]['address']
-
- # Verify we can SSH into container
- try:
- cmd = shlex.split(
- "ssh -i {}/id_lxd_rsa {} root@{} hostname".format(
- tmp,
- "-oStrictHostKeyChecking=no",
- ipaddr,
- )
- )
- subprocess.check_call(cmd)
- except subprocess.CalledProcessError as e:
- logging.debug(e)
- assert False
-
- # Delete the container
- base.destroy_lxd_container(container)
-
- # Verify the container is deleted
- client = base.get_lxd_client()
- assert client.containers.exists(hostname) is False
-
- # Verify the container profile is deleted
- assert client.profiles.exists(hostname) is False
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-
-# 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.
-
-"""
-Test N2VC's ssh key generation
-"""
-import n2vc
-import pytest
-from . import base
-import uuid
-
-
-@pytest.mark.asyncio
-async def test_model_create():
- """Test the creation of a new model."""
- client = base.get_n2vc()
-
- model_name = "test-{}".format(
- uuid.uuid4().hex[-4:],
- )
-
- pytest.assume(await client.CreateNetworkService(model_name))
- pytest.assume(await client.DestroyNetworkService(model_name))
- pytest.assume(await client.logout())
-
-
-@pytest.mark.asyncio
-async def test_destroy_non_existing_network_service():
- """Destroy a model that doesn't exist."""
-
- client = base.get_n2vc()
-
- model_name = "test-{}".format(
- uuid.uuid4().hex[-4:],
- )
-
- with pytest.raises(n2vc.vnf.NetworkServiceDoesNotExist):
- pytest.assume(await client.DestroyNetworkService(model_name))
-
- pytest.assume(await client.logout())
-
-
-@pytest.mark.asyncio
-async def test_model_create_duplicate():
- """Create a new model, and try to create the same model."""
- client = base.get_n2vc()
-
- model_name = "test-{}".format(
- uuid.uuid4().hex[-4:],
- )
-
- # Try to recreate bug 628
- for x in range(0, 1000):
- model = await client.get_model(model_name)
- pytest.assume(model)
-
- pytest.assume(await client.DestroyNetworkService(model_name))
- pytest.assume(await client.logout())
+++ /dev/null
-# Copyright 2019 Canonical Ltd.
-
-# 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.
-
-"""
-Test N2VC's ssh key generation
-"""
-import os
-import pytest
-from . import base
-import tempfile
-
-
-@pytest.mark.asyncio
-async def test_ssh_keygen(monkeypatch):
- with tempfile.TemporaryDirectory() as tmpdirname:
- monkeypatch.setitem(os.environ, "HOME", tmpdirname)
-
- client = base.get_n2vc()
-
- public_key = await client.GetPublicKey()
- assert len(public_key)