Skip to content

Commit

Permalink
1.0 release and Beta! Numerous changes, read commit message for the d…
Browse files Browse the repository at this point in the history
…etails :)

- Platform interfaces now subclass Interface
- Several functions have been moved from VsphereInterface to Interface
- Added Thresholds to Infrastructure specification
- Moved get_vlan to utils from VsphereInterface
- Changed logging output to not include date and add more space for class name
- Added date, improved platform information and hostname to logging initialization info
- Fixed numerous bugs, including many that were introduced in the last 5-10 commits
- Updated and renamed interface UML diagram, includes Docker now and changes to vSphere
- Added classifiers to setup.py and updated status to reflect Beta
- Changed version numbers on stable classes to 1.0.0
- Improved comments and cleaned up code

NOTE: vm_snapshots still isn't fully stable, notably the get functionality isn't working yet.
  • Loading branch information
GhostofGoes committed Apr 29, 2017
1 parent a9c81dc commit 333f679
Show file tree
Hide file tree
Showing 18 changed files with 309 additions and 279 deletions.
2 changes: 1 addition & 1 deletion adles/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

__version__ = "0.13.10"
__version__ = "1.0.0"
__author__ = "Christopher Goes"
__email__ = "<[email protected]>"
__license__ = "Apache 2.0"
Expand Down
32 changes: 22 additions & 10 deletions adles/interfaces/docker_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,29 @@
exit(0)

import adles.utils as utils
from adles.interfaces import Interface


class DockerInterface:
class DockerInterface(Interface):
""" Generic interface for the Docker platform """

__version__ = "0.1.2"
__version__ = "0.2.0"

# noinspection PyMissingConstructor
def __init__(self, infra, spec):
"""
:param infra: Dict of infrastructure information
:param spec: Dict of a parsed specification
:param dict infra: Dict of infrastructure information
:param dict spec: Dict of a parsed specification
"""
self._log = logging.getLogger('DockerInterface')
self._log.debug("Initializing DockerInterface %s", DockerInterface.__version__)

self.infra = infra
self.spec = spec
self.metadata = spec["metadata"]
self.services = spec["services"]
self.networks = spec["networks"]
self.folders = spec["folders"]
self.infra = infra

# TODO: this is initial testing of client
# If needed, a wrapper class that simplifies the creation of containers will be made
Expand All @@ -54,11 +55,10 @@ def __init__(self, infra, spec):
# Verify the connection to the client
self.client.ping()

# TODO: add interface-specific loggers so we know what interface is outputting
self._log.debug("System info : %s", str(self.client.info()))
self._log.debug("System version : %s", str(self.client.version()))

# Authenticate to registry, if configured
# Authenticate to registry, if configured (TODO)
if "registry" in self.infra:
reg = self.infra["registry"]
reg_logins = utils.read_json(reg["login-file"])
Expand All @@ -74,8 +74,20 @@ def create_masters(self):
def deploy_environment(self):
pass

def cleanup_masters(self, network_cleanup):
def cleanup_masters(self, network_cleanup=False):
pass

def cleanup_environment(self, network_cleanup):
def cleanup_environment(self, network_cleanup=False):
pass

def __repr__(self):
return str("DockerInterface(%s, %s)" % (str(self.infra), str(self.spec)))

def __str__(self):
return str(self.client.info() + "\nVersion:\t" + self.client.version())

def __hash__(self):
return hash(str(self))

def __eq__(self, other):
return isinstance(other, self.__class__) and self.client == other.client
109 changes: 106 additions & 3 deletions adles/interfaces/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,33 @@
from adles.utils import time_execution


# TODO: subclass this for platform-specific interfaces
class Interface:
""" Generic interface used to uniformly interact with platform-specific interfaces. """
__version__ = "1.1.0"

__version__ = "1.0.1" # This should match version of infrastructure-specification.yaml
# Names/prefixes
master_prefix = "(MASTER) "
master_root_name = "MASTER-FOLDERS"

def __init__(self, spec, infra):
"""
:param dict spec: Full exercise specification
:param dict infra: Full infrastructure configuration
"""
self._log = logging.getLogger('PlatformInterface')
self._log.debug("Initializing Interface %s", Interface.__version__)

self.interfaces = [] # List of instantiated platform interfaces
self.metadata = spec["metadata"] # Save the exercise specification metadata
self.infra = infra # Save the infrastructure configuration
self.thresholds = {}
self.networks = {}
self.groups = {}

# Select the Interface to use based on the specified infrastructure platform
for platform, config in infra.items():
if platform == "vmware-vsphere":
# TODO: support multiple vSphere instances (e.g for remote labs)
from .vsphere_interface import VsphereInterface
self.interfaces.append(VsphereInterface(config, spec))
elif platform == "docker":
Expand Down Expand Up @@ -83,8 +91,103 @@ def cleanup_environment(self, network_cleanup=False):
for i in self.interfaces:
i.cleanup_masters(network_cleanup=network_cleanup)

def _instances_handler(self, spec, obj_name, obj_type):
"""
Determines number of instances and optional prefix using specification
:param dict spec: Dict of folder
:param str obj_name: Name of the thing being handled
:param str obj_type: Type of the thing being handled (folder | service)
:return: Number of instances, Prefix
:rtype: tuple(int, str)
"""
num = 1
prefix = ""
if "instances" in spec:
if type(spec["instances"]) == int:
num = int(spec["instances"])
else:
if "prefix" in spec["instances"]:
prefix = str(spec["instances"]["prefix"])
if "number" in spec["instances"]:
num = int(spec["instances"]["number"])
elif "size-of" in spec["instances"]:
# size_of = spec["instances"]["size-of"])
# num = int(self._get_group(size_of).size
# if num < 1:
num = 1 # TODO: WORKAROUND FOR AD-GROUPS
else:
self._log.error("Unknown instances specification: %s", str(spec["instances"]))
num = 0

# Check if the number of instances exceeds the configured thresholds for the interface
thr = self.thresholds[obj_type]
if num > thr["error"]:
self._log.error("%d instances of %s '%s' is beyond the configured %s threshold of %d",
num, obj_type, obj_name, self.__name__, thr["error"])
raise Exception("Threshold exceeded")
elif num > thr["warn"]:
self._log.warning("%d instances of %s '%s' is beyond the configured %s threshold of %d",
num, obj_type, obj_name, self.__name__, thr["warn"])
return num, prefix

def _path(self, path, name):
"""
Generates next step of the path for deployment of Masters
:param str path: Current path
:param str name: Name to add to the path
:return: The updated path
:rtype: str
"""
return str(path + '/' + self.master_prefix + name)

@staticmethod
def _is_enabled(spec):
"""
Determines if a spec is enabled
:param dict spec:
:return: If the spec is enabled
:rtype: bool
"""
if "enabled" in spec:
return bool(spec["enabled"])
else:
return True

def _determine_net_type(self, network_label):
"""
Determines the type of a network
:param str network_label: Name of network to determine type of
:return: Type of the network ("generic-networks" | "unique-networks")
:rtype: str
"""
for net_name, net_value in self.networks.items():
vals = set(k for k in net_value)
if network_label in vals:
return net_name
self._log.error("Could not find type for network '%s'", network_label)
return ""

def _get_group(self, group_name):
"""
Provides a uniform way to get information about normal groups and template groups
:param str group_name: Name of the group
:return: Group object
:rtype: :class:`Group`
"""
from adles.group import Group
if group_name in self.groups:
g = self.groups[group_name]
if isinstance(g, Group): # Normal groups
return g
elif isinstance(g, list): # Template groups
return g[0]
else:
self._log.error("Unknown type for group '%s': %s", str(group_name), str(type(g)))
else:
self._log.error("Could not get group '%s' from groups", group_name)

def __repr__(self):
return "Interface(%s,%s" % (str(self.interfaces), str(self.infra))
return "Interface(%s, %s)" % (str(self.interfaces), str(self.infra))

def __str__(self):
return str([x for x in self.infra.keys()])
Expand Down
Loading

0 comments on commit 333f679

Please sign in to comment.