def is_bootstrapped():
result = subprocess.run(['juju', 'switch'], stdout=subprocess.PIPE)
return (
- result.returncode == 0 and
- len(result.stdout.decode().strip()) > 0)
+ result.returncode == 0 and len(result.stdout.decode().strip()) > 0)
bootstrapped = pytest.mark.skipif(
logging.debug(
"[{}] {}".format(now.strftime('%Y-%m-%dT%H:%M:%S'), msg)
)
- # print(
- # "[{}] {}".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():
name,
)
- private_key_path, public_key_path = find_juju_ssh_keys()
+ private_key_path, public_key_path = find_n2vc_ssh_keys()
try:
# create profile w/cloud-init and juju ssh key
)
)
+ 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.
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.
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."""
self.ns_name = self.nsd['name']
self.vnf_name = self.vnfd['name']
- # Hard-coded to default for now, but this may change in the future.
- self.model = "default"
-
self.charms = {}
self.parse_vnf_descriptor()
assert self.charms is not {}
"""
debug("Running teardown_class...")
try:
+
debug("Destroying LXD containers...")
for application in self.state:
if self.state[application]['container']:
# Logout of N2VC
if self.n2vc:
- debug("Logging out of N2VC...")
+ debug("teardown_class(): Logging out of N2VC...")
yield from self.n2vc.logout()
- debug("Logging out of N2VC...done.")
+ debug("teardown_class(): Logging out of N2VC...done.")
+
debug("Running teardown_class...done.")
except Exception as ex:
debug("Exception in teardown_class: {}".format(ex))
# Make sure the charm snap is installed
try:
subprocess.check_call(['which', 'charm'])
- except subprocess.CalledProcessError as e:
+ except subprocess.CalledProcessError:
raise Exception("charm snap not installed.")
if charm not in self.artifacts:
builds = get_charm_path()
if not os.path.exists("{}/builds/{}".format(builds, charm)):
- cmd = "charm build {}/{} -o {}/".format(
+ cmd = "charm build --no-local-layers {}/{} -o {}/".format(
get_layer_path(),
charm,
builds,
)
subprocess.check_call(shlex.split(cmd))
- self.artifacts[charm] = {
- 'tmpdir': builds,
- 'charm': "{}/builds/{}".format(builds, charm),
- }
except subprocess.CalledProcessError as e:
- raise Exception("charm build failed: {}.".format(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']
if not self.n2vc:
self.n2vc = get_n2vc(loop=loop)
- vnf_name = self.n2vc.FormatApplicationName(
+ 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 = {
+ 'host': hostname,
+ 'user': 'ubuntu',
+ }
+
await self.n2vc.DeployCharms(
self.ns_name,
- vnf_name,
+ application,
self.vnfd,
self.get_charm(charm),
params,
- {},
+ machine_spec,
self.n2vc_callback,
)
@classmethod
async def configure_proxy_charm(self, *args):
+ """Configure a container for use via ssh."""
(model, application, _, _) = args
try:
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:
- await self.n2vc.RemoveCharms(self.model, application)
+
+ 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(
# Logout of N2VC
try:
- debug("Logging out of N2VC...")
+ debug("stop(): Logging out of N2VC...")
await self.n2vc.logout()
self.n2vc = None
- debug("Logging out of N2VC...Done.")
+ debug("stop(): Logging out of N2VC...Done.")
except Exception as ex:
debug(ex)
)
await self.n2vc.ExecutePrimitive(
- self.model,
+ self.ns_name,
application,
"config",
None,
Re-run those actions so we can inspect the status.
"""
uuids = await self.n2vc.ExecuteInitialPrimitives(
- self.model,
+ self.ns_name,
application,
init_config,
)
debug("Collecting metrics for {}".format(application))
metrics = await self.n2vc.GetMetrics(
- self.model,
+ self.ns_name,
application,
)
debug("Getting status of {} ({})...".format(uid, status))
status = await self.n2vc.GetPrimitiveStatus(
- self.model,
+ self.ns_name,
uid,
)
debug("...state of {} is {}".format(uid, status))
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))