diff --git a/gns3dms/__init__.py b/gns3dms/__init__.py
deleted file mode 100644
index cf426f79..00000000
--- a/gns3dms/__init__.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-# __version__ is a human-readable version number.
-
-# __version_info__ is a four-tuple for programmatic comparison. The first
-# three numbers are the components of the version number. The fourth
-# is zero for an official release, positive for a development branch,
-# or negative for a release candidate or beta (after the base version
-# number has been incremented)
-
-from .version import __version__
diff --git a/gns3dms/cloud/__init__.py b/gns3dms/cloud/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/gns3dms/cloud/base_cloud_ctrl.py b/gns3dms/cloud/base_cloud_ctrl.py
deleted file mode 100644
index 0ad74af1..00000000
--- a/gns3dms/cloud/base_cloud_ctrl.py
+++ /dev/null
@@ -1,340 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-"""
-Base cloud controller class.
-
-Base class for interacting with Cloud APIs to create and manage cloud
-instances.
-
-"""
-from collections import namedtuple
-import hashlib
-import os
-import logging
-from io import StringIO, BytesIO
-
-from libcloud.compute.base import NodeAuthSSHKey
-from libcloud.storage.types import ContainerAlreadyExistsError, ContainerDoesNotExistError, ObjectDoesNotExistError
-
-from .exceptions import ItemNotFound, KeyPairExists, MethodNotAllowed
-from .exceptions import OverLimit, BadRequest, ServiceUnavailable
-from .exceptions import Unauthorized, ApiError
-
-
-KeyPair = namedtuple("KeyPair", ['name'], verbose=False)
-log = logging.getLogger(__name__)
-
-
-def parse_exception(exception):
- """
- Parse the exception to separate the HTTP status code from the text.
-
- Libcloud raises many exceptions of the form:
- Exception(" ")
-
- in lieu of raising specific incident-based exceptions.
-
- """
-
- e_str = str(exception)
-
- try:
- status = int(e_str[0:3])
- error_text = e_str[3:]
-
- except ValueError:
- status = None
- error_text = e_str
-
- return status, error_text
-
-
-class BaseCloudCtrl(object):
-
- """ Base class for interacting with a cloud provider API. """
-
- http_status_to_exception = {
- 400: BadRequest,
- 401: Unauthorized,
- 404: ItemNotFound,
- 405: MethodNotAllowed,
- 413: OverLimit,
- 500: ApiError,
- 503: ServiceUnavailable
- }
-
- GNS3_CONTAINER_NAME = 'GNS3'
-
- def __init__(self, username, api_key):
- self.username = username
- self.api_key = api_key
-
- def _handle_exception(self, status, error_text, response_overrides=None):
- """ Raise an exception based on the HTTP status. """
-
- if response_overrides:
- if status in response_overrides:
- raise response_overrides[status](error_text)
-
- raise self.http_status_to_exception[status](error_text)
-
- def authenticate(self):
- """ Validate cloud account credentials. Return boolean. """
- raise NotImplementedError
-
- def list_sizes(self):
- """ Return a list of NodeSize objects. """
-
- return self.driver.list_sizes()
-
- def list_flavors(self):
- """ Return an iterable of flavors """
-
- raise NotImplementedError
-
- def create_instance(self, name, size_id, image_id, keypair):
- """
- Create a new instance with the supplied attributes.
-
- Return a Node object.
-
- """
- try:
- image = self.get_image(image_id)
- if image is None:
- raise ItemNotFound("Image not found")
-
- size = self.driver.ex_get_size(size_id)
-
- args = {
- "name": name,
- "size": size,
- "image": image,
- }
-
- if keypair is not None:
- auth_key = NodeAuthSSHKey(keypair.public_key)
- args["auth"] = auth_key
- args["ex_keyname"] = name
-
- return self.driver.create_node(**args)
-
- except Exception as e:
- status, error_text = parse_exception(e)
-
- if status:
- self._handle_exception(status, error_text)
- else:
- log.error("create_instance method raised an exception: {}".format(e))
- log.error('image id {}'.format(image))
-
- def delete_instance(self, instance):
- """ Delete the specified instance. Returns True or False. """
-
- try:
- return self.driver.destroy_node(instance)
-
- except Exception as e:
-
- status, error_text = parse_exception(e)
-
- if status:
- self._handle_exception(status, error_text)
- else:
- raise e
-
- def get_instance(self, instance):
- """ Return a Node object representing the requested instance. """
-
- for i in self.driver.list_nodes():
- if i.id == instance.id:
- return i
-
- raise ItemNotFound("Instance not found")
-
- def list_instances(self):
- """ Return a list of instances in the current region. """
-
- try:
- return self.driver.list_nodes()
- except Exception as e:
- log.error("list_instances returned an error: {}".format(e))
-
- def create_key_pair(self, name):
- """ Create and return a new Key Pair. """
-
- response_overrides = {
- 409: KeyPairExists
- }
- try:
- return self.driver.create_key_pair(name)
-
- except Exception as e:
- status, error_text = parse_exception(e)
- if status:
- self._handle_exception(status, error_text, response_overrides)
- else:
- raise e
-
- def delete_key_pair(self, keypair):
- """ Delete the keypair. Returns True or False. """
-
- try:
- return self.driver.delete_key_pair(keypair)
-
- except Exception as e:
- status, error_text = parse_exception(e)
- if status:
- self._handle_exception(status, error_text)
- else:
- raise e
-
- def delete_key_pair_by_name(self, keypair_name):
- """ Utility method to incapsulate boilerplate code """
-
- kp = KeyPair(name=keypair_name)
- return self.delete_key_pair(kp)
-
- def list_key_pairs(self):
- """ Return a list of Key Pairs. """
-
- return self.driver.list_key_pairs()
-
- def upload_file(self, file_path, cloud_object_name):
- """
- Uploads file to cloud storage (if it is not identical to a file already in cloud storage).
- :param file_path: path to file to upload
- :param cloud_object_name: name of file saved in cloud storage
- :return: True if file was uploaded, False if it was skipped because it already existed and was identical
- """
- try:
- gns3_container = self.storage_driver.create_container(self.GNS3_CONTAINER_NAME)
- except ContainerAlreadyExistsError:
- gns3_container = self.storage_driver.get_container(self.GNS3_CONTAINER_NAME)
-
- with open(file_path, 'rb') as file:
- local_file_hash = hashlib.md5(file.read()).hexdigest()
-
- cloud_hash_name = cloud_object_name + '.md5'
- cloud_objects = [obj.name for obj in gns3_container.list_objects()]
-
- # if the file and its hash are in object storage, and the local and storage file hashes match
- # do not upload the file, otherwise upload it
- if cloud_object_name in cloud_objects and cloud_hash_name in cloud_objects:
- hash_object = gns3_container.get_object(cloud_hash_name)
- cloud_object_hash = ''
- for chunk in hash_object.as_stream():
- cloud_object_hash += chunk.decode('utf8')
-
- if cloud_object_hash == local_file_hash:
- return False
-
- file.seek(0)
- self.storage_driver.upload_object_via_stream(file, gns3_container, cloud_object_name)
- self.storage_driver.upload_object_via_stream(StringIO(local_file_hash), gns3_container, cloud_hash_name)
- return True
-
- def list_projects(self):
- """
- Lists projects in cloud storage
- :return: Dictionary where project names are keys and values are names of objects in storage
- """
-
- try:
- gns3_container = self.storage_driver.get_container(self.GNS3_CONTAINER_NAME)
- projects = {
- obj.name.replace('projects/', '').replace('.zip', ''): obj.name
- for obj in gns3_container.list_objects()
- if obj.name.startswith('projects/') and obj.name[-4:] == '.zip'
- }
- return projects
- except ContainerDoesNotExistError:
- return []
-
- def download_file(self, file_name, destination=None):
- """
- Downloads file from cloud storage. If a file exists at destination, and it is identical to the file in cloud
- storage, it is not downloaded.
- :param file_name: name of file in cloud storage to download
- :param destination: local path to save file to (if None, returns file contents as a file-like object)
- :return: A file-like object if file contents are returned, or None if file is saved to filesystem
- """
-
- gns3_container = self.storage_driver.get_container(self.GNS3_CONTAINER_NAME)
- storage_object = gns3_container.get_object(file_name)
-
- if destination is not None:
- if os.path.isfile(destination):
- # if a file exists at destination and its hash matches that of the
- # file in cloud storage, don't download it
- with open(destination, 'rb') as f:
- local_file_hash = hashlib.md5(f.read()).hexdigest()
-
- hash_object = gns3_container.get_object(file_name + '.md5')
- cloud_object_hash = ''
- for chunk in hash_object.as_stream():
- cloud_object_hash += chunk.decode('utf8')
-
- if local_file_hash == cloud_object_hash:
- return
-
- storage_object.download(destination)
- else:
- contents = b''
-
- for chunk in storage_object.as_stream():
- contents += chunk
-
- return BytesIO(contents)
-
- def find_storage_image_names(self, images_to_find):
- """
- Maps names of image files to their full name in cloud storage
- :param images_to_find: list of image names to find
- :return: A dictionary where keys are image names, and values are the corresponding names of
- the files in cloud storage
- """
- gns3_container = self.storage_driver.get_container(self.GNS3_CONTAINER_NAME)
- images_in_storage = [obj.name for obj in gns3_container.list_objects() if obj.name.startswith('images/')]
-
- images = {}
- for image_name in images_to_find:
- images_with_same_name =\
- list(filter(lambda storage_image_name: storage_image_name.endswith(image_name), images_in_storage))
-
- if len(images_with_same_name) == 1:
- images[image_name] = images_with_same_name[0]
- else:
- raise Exception('Image does not exist in cloud storage or is duplicated')
-
- return images
-
- def delete_file(self, file_name):
- gns3_container = self.storage_driver.get_container(self.GNS3_CONTAINER_NAME)
-
- try:
- object_to_delete = gns3_container.get_object(file_name)
- object_to_delete.delete()
- except ObjectDoesNotExistError:
- pass
-
- try:
- hash_object = gns3_container.get_object(file_name + '.md5')
- hash_object.delete()
- except ObjectDoesNotExistError:
- pass
diff --git a/gns3dms/cloud/exceptions.py b/gns3dms/cloud/exceptions.py
deleted file mode 100644
index 65d65f9f..00000000
--- a/gns3dms/cloud/exceptions.py
+++ /dev/null
@@ -1,67 +0,0 @@
-""" Exception classes for CloudCtrl classes. """
-
-
-class ApiError(Exception):
-
- """ Raised when the server returns 500 Compute Error. """
- pass
-
-
-class BadRequest(Exception):
-
- """ Raised when the server returns 400 Bad Request. """
- pass
-
-
-class ComputeFault(Exception):
-
- """ Raised when the server returns 400|500 Compute Fault. """
- pass
-
-
-class Forbidden(Exception):
-
- """ Raised when the server returns 403 Forbidden. """
- pass
-
-
-class ItemNotFound(Exception):
-
- """ Raised when the server returns 404 Not Found. """
- pass
-
-
-class KeyPairExists(Exception):
-
- """ Raised when the server returns 409 Conflict Key pair exists. """
- pass
-
-
-class MethodNotAllowed(Exception):
-
- """ Raised when the server returns 405 Method Not Allowed. """
- pass
-
-
-class OverLimit(Exception):
-
- """ Raised when the server returns 413 Over Limit. """
- pass
-
-
-class ServerCapacityUnavailable(Exception):
-
- """ Raised when the server returns 503 Server Capacity Uavailable. """
- pass
-
-
-class ServiceUnavailable(Exception):
-
- """ Raised when the server returns 503 Service Unavailable. """
- pass
-
-
-class Unauthorized(Exception):
-
- """ Raised when the server returns 401 Unauthorized. """
- pass
diff --git a/gns3dms/cloud/rackspace_ctrl.py b/gns3dms/cloud/rackspace_ctrl.py
deleted file mode 100644
index aee7f46d..00000000
--- a/gns3dms/cloud/rackspace_ctrl.py
+++ /dev/null
@@ -1,259 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-""" Interacts with Rackspace API to create and manage cloud instances. """
-
-from .base_cloud_ctrl import BaseCloudCtrl
-import json
-import requests
-from libcloud.compute.drivers.rackspace import ENDPOINT_ARGS_MAP
-from libcloud.compute.providers import get_driver
-from libcloud.compute.types import Provider
-from libcloud.storage.providers import get_driver as get_storage_driver
-from libcloud.storage.types import Provider as StorageProvider
-
-from .exceptions import ItemNotFound, ApiError
-from ..version import __version__
-
-from collections import OrderedDict
-
-import logging
-log = logging.getLogger(__name__)
-
-RACKSPACE_REGIONS = [{ENDPOINT_ARGS_MAP[k]['region']: k} for k in
- ENDPOINT_ARGS_MAP]
-
-
-class RackspaceCtrl(BaseCloudCtrl):
-
- """ Controller class for interacting with Rackspace API. """
-
- def __init__(self, username, api_key, *args, **kwargs):
- super(RackspaceCtrl, self).__init__(username, api_key)
-
- # set this up so it can be swapped out with a mock for testing
- self.post_fn = requests.post
- self.driver_cls = get_driver(Provider.RACKSPACE)
- self.storage_driver_cls = get_storage_driver(StorageProvider.CLOUDFILES)
-
- self.driver = None
- self.storage_driver = None
- self.region = None
- self.instances = {}
-
- self.authenticated = False
- self.identity_ep = \
- "https://identity.api.rackspacecloud.com/v2.0/tokens"
-
- self.regions = []
- self.token = None
- self.tenant_id = None
- self.flavor_ep = "https://dfw.servers.api.rackspacecloud.com/v2/{username}/flavors"
- self._flavors = OrderedDict([
- ('2', '512MB, 1 VCPU'),
- ('3', '1GB, 1 VCPU'),
- ('4', '2GB, 2 VCPUs'),
- ('5', '4GB, 2 VCPUs'),
- ('6', '8GB, 4 VCPUs'),
- ('7', '15GB, 6 VCPUs'),
- ('8', '30GB, 8 VCPUs'),
- ('performance1-1', '1GB Performance, 1 VCPU'),
- ('performance1-2', '2GB Performance, 2 VCPUs'),
- ('performance1-4', '4GB Performance, 4 VCPUs'),
- ('performance1-8', '8GB Performance, 8 VCPUs'),
- ('performance2-15', '15GB Performance, 4 VCPUs'),
- ('performance2-30', '30GB Performance, 8 VCPUs'),
- ('performance2-60', '60GB Performance, 16 VCPUs'),
- ('performance2-90', '90GB Performance, 24 VCPUs'),
- ('performance2-120', '120GB Performance, 32 VCPUs',)
- ])
-
- def authenticate(self):
- """
- Submit username and api key to API service.
-
- If authentication is successful, set self.regions and self.token.
- Return boolean.
-
- """
-
- self.authenticated = False
-
- if len(self.username) < 1:
- return False
-
- if len(self.api_key) < 1:
- return False
-
- data = json.dumps({
- "auth": {
- "RAX-KSKEY:apiKeyCredentials": {
- "username": self.username,
- "apiKey": self.api_key
- }
- }
- })
-
- headers = {
- 'Content-type': 'application/json',
- 'Accept': 'application/json'
- }
-
- response = self.post_fn(self.identity_ep, data=data, headers=headers)
-
- if response.status_code == 200:
-
- api_data = response.json()
- self.token = self._parse_token(api_data)
-
- if self.token:
- self.authenticated = True
- user_regions = self._parse_endpoints(api_data)
- self.regions = self._make_region_list(user_regions)
- self.tenant_id = self._parse_tenant_id(api_data)
-
- else:
- self.regions = []
- self.token = None
-
- response.connection.close()
-
- return self.authenticated
-
- def list_regions(self):
- """ Return a list the regions available to the user. """
-
- return self.regions
-
- def list_flavors(self):
- """ Return the dictionary containing flavors id and names """
-
- return self._flavors
-
- def _parse_endpoints(self, api_data):
- """
- Parse the JSON-encoded data returned by the Identity Service API.
-
- Return a list of regions available for Compute v2.
-
- """
-
- region_codes = []
-
- for ep_type in api_data['access']['serviceCatalog']:
- if ep_type['name'] == "cloudServersOpenStack" \
- and ep_type['type'] == "compute":
-
- for ep in ep_type['endpoints']:
- if ep['versionId'] == "2":
- region_codes.append(ep['region'])
-
- return region_codes
-
- def _parse_token(self, api_data):
- """ Parse the token from the JSON-encoded data returned by the API. """
-
- try:
- token = api_data['access']['token']['id']
- except KeyError:
- return None
-
- return token
-
- def _parse_tenant_id(self, api_data):
- """ """
- try:
- roles = api_data['access']['user']['roles']
- for role in roles:
- if 'tenantId' in role and role['name'] == 'compute:default':
- return role['tenantId']
- return None
- except KeyError:
- return None
-
- def _make_region_list(self, region_codes):
- """
- Make a list of regions for use in the GUI.
-
- Returns a list of key-value pairs in the form:
- :
- eg,
- [
- {'DFW': 'dfw'}
- {'ORD': 'ord'},
- ...
- ]
-
- """
-
- region_list = []
-
- for ep in ENDPOINT_ARGS_MAP:
- if ENDPOINT_ARGS_MAP[ep]['region'] in region_codes:
- region_list.append({ENDPOINT_ARGS_MAP[ep]['region']: ep})
-
- return region_list
-
- def set_region(self, region):
- """ Set self.region and self.driver. Returns True or False. """
-
- try:
- self.driver = self.driver_cls(self.username, self.api_key,
- region=region)
- self.storage_driver = self.storage_driver_cls(self.username, self.api_key,
- region=region)
-
- except ValueError:
- return False
-
- self.region = region
- return True
-
- def get_image(self, image_id):
- return self.driver.get_image(image_id)
-
-
-def get_provider(cloud_settings):
- """
- Utility function to retrieve a cloud provider instance already authenticated and with the
- region set
-
- :param cloud_settings: cloud settings dictionary
- :return: a provider instance or None on errors
- """
- try:
- username = cloud_settings['cloud_user_name']
- apikey = cloud_settings['cloud_api_key']
- region = cloud_settings['cloud_region']
- except KeyError as e:
- log.error("Unable to create cloud provider: {}".format(e))
- return
-
- provider = RackspaceCtrl(username, apikey)
-
- if not provider.authenticate():
- log.error("Authentication failed for cloud provider")
- return
-
- if not region:
- region = provider.list_regions().values()[0]
-
- if not provider.set_region(region):
- log.error("Unable to set cloud provider region")
- return
-
- return provider
diff --git a/gns3dms/main.py b/gns3dms/main.py
deleted file mode 100644
index d7d0fc30..00000000
--- a/gns3dms/main.py
+++ /dev/null
@@ -1,402 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-# __version__ is a human-readable version number.
-
-# __version_info__ is a four-tuple for programmatic comparison. The first
-# three numbers are the components of the version number. The fourth
-# is zero for an official release, positive for a development branch,
-# or negative for a release candidate or beta (after the base version
-# number has been incremented)
-
-"""
-Monitors communication with the GNS3 client via tmp file. Will terminate the instance if
-communication is lost.
-"""
-
-import os
-import sys
-import time
-import getopt
-import datetime
-import logging
-import signal
-import configparser
-from logging.handlers import *
-from os.path import expanduser
-
-SCRIPT_NAME = os.path.basename(__file__)
-
-# Is the full path when used as an import
-SCRIPT_PATH = os.path.dirname(__file__)
-
-if not SCRIPT_PATH:
- SCRIPT_PATH = os.path.join(os.path.dirname(os.path.abspath(
- sys.argv[0])))
-
-
-EXTRA_LIB = "%s/modules" % (SCRIPT_PATH)
-sys.path.append(EXTRA_LIB)
-
-from . import cloud
-from rackspace_cloud import Rackspace
-
-LOG_NAME = "gns3dms"
-log = None
-
-sys.path.append(EXTRA_LIB)
-
-import daemon
-
-my_daemon = None
-
-usage = """
-USAGE: %s
-
-Options:
-
- -d, --debug Enable debugging
- -v, --verbose Enable verbose logging
- -h, --help Display this menu :)
-
- --cloud_api_key Rackspace API key
- --cloud_user_name
-
- --instance_id ID of the Rackspace instance to terminate
- --cloud_region Region of instance
-
- --dead_time How long in seconds can the communication lose exist before we
- shutdown this instance.
- Default:
- Example --dead_time=3600 (60 minutes)
-
- --check-interval Defaults to --dead_time, used for debugging
-
- --init-wait Inital wait time, how long before we start pulling the file.
- Default: 300 (5 min)
- Example --init-wait=300
-
- --file The file we monitor for updates
-
- -k Kill previous instance running in background
- --background Run in background
-
-""" % (SCRIPT_NAME)
-
-# Parse cmd line options
-
-
-def parse_cmd_line(argv):
- """
- Parse command line arguments
-
- argv: Pass in cmd line arguments
- """
-
- short_args = "dvhk"
- long_args = ("debug",
- "verbose",
- "help",
- "cloud_user_name=",
- "cloud_api_key=",
- "instance_id=",
- "region=",
- "dead_time=",
- "init-wait=",
- "check-interval=",
- "file=",
- "background",
- )
- try:
- opts, extra_opts = getopt.getopt(argv[1:], short_args, long_args)
- except getopt.GetoptError as e:
- print("Unrecognized command line option or missing required argument: %s" % (e))
- print(usage)
- sys.exit(2)
-
- cmd_line_option_list = {}
- cmd_line_option_list["debug"] = False
- cmd_line_option_list["verbose"] = True
- cmd_line_option_list["cloud_user_name"] = None
- cmd_line_option_list["cloud_api_key"] = None
- cmd_line_option_list["instance_id"] = None
- cmd_line_option_list["region"] = None
- cmd_line_option_list["dead_time"] = 60 * 60 # minutes
- cmd_line_option_list["check-interval"] = None
- cmd_line_option_list["init-wait"] = 5 * 60
- cmd_line_option_list["file"] = None
- cmd_line_option_list["shutdown"] = False
- cmd_line_option_list["daemon"] = False
- cmd_line_option_list['starttime'] = datetime.datetime.now()
-
- if sys.platform == "linux":
- cmd_line_option_list['syslog'] = "/dev/log"
- elif sys.platform == "osx":
- cmd_line_option_list['syslog'] = "/var/run/syslog"
- else:
- cmd_line_option_list['syslog'] = ('localhost', 514)
-
- get_gns3secrets(cmd_line_option_list)
- cmd_line_option_list["dead_time"] = int(cmd_line_option_list["dead_time"])
-
- for opt, val in opts:
- if (opt in ("-h", "--help")):
- print(usage)
- sys.exit(0)
- elif (opt in ("-d", "--debug")):
- cmd_line_option_list["debug"] = True
- elif (opt in ("-v", "--verbose")):
- cmd_line_option_list["verbose"] = True
- elif (opt in ("--cloud_user_name")):
- cmd_line_option_list["cloud_user_name"] = val
- elif (opt in ("--cloud_api_key")):
- cmd_line_option_list["cloud_api_key"] = val
- elif (opt in ("--instance_id")):
- cmd_line_option_list["instance_id"] = val
- elif (opt in ("--region")):
- cmd_line_option_list["region"] = val
- elif (opt in ("--dead_time")):
- cmd_line_option_list["dead_time"] = int(val)
- elif (opt in ("--check-interval")):
- cmd_line_option_list["check-interval"] = int(val)
- elif (opt in ("--init-wait")):
- cmd_line_option_list["init-wait"] = int(val)
- elif (opt in ("--file")):
- cmd_line_option_list["file"] = val
- elif (opt in ("-k")):
- cmd_line_option_list["shutdown"] = True
- elif (opt in ("--background")):
- cmd_line_option_list["daemon"] = True
-
- if cmd_line_option_list["shutdown"] is False:
-
- if cmd_line_option_list["check-interval"] is None:
- cmd_line_option_list["check-interval"] = cmd_line_option_list["dead_time"] + 120
-
- if cmd_line_option_list["cloud_user_name"] is None:
- print("You need to specify a username!!!!")
- print(usage)
- sys.exit(2)
-
- if cmd_line_option_list["cloud_api_key"] is None:
- print("You need to specify an apikey!!!!")
- print(usage)
- sys.exit(2)
-
- if cmd_line_option_list["file"] is None:
- print("You need to specify a file to watch!!!!")
- print(usage)
- sys.exit(2)
-
- if cmd_line_option_list["instance_id"] is None:
- print("You need to specify an instance_id")
- print(usage)
- sys.exit(2)
-
- if cmd_line_option_list["cloud_region"] is None:
- print("You need to specify a cloud_region")
- print(usage)
- sys.exit(2)
-
- return cmd_line_option_list
-
-
-def get_gns3secrets(cmd_line_option_list):
- """
- Load cloud credentials from .gns3secrets
- """
-
- gns3secret_paths = [
- os.path.join(os.path.expanduser("~"), '.config', 'GNS3'),
- SCRIPT_PATH,
- ]
-
- config = configparser.ConfigParser()
-
- for gns3secret_path in gns3secret_paths:
- gns3secret_file = "%s/cloud.conf" % (gns3secret_path)
- if os.path.isfile(gns3secret_file):
- config.read(gns3secret_file)
-
- try:
- for key, value in config.items("CLOUD_SERVER"):
- cmd_line_option_list[key] = value.strip()
- except configparser.NoSectionError:
- pass
-
-
-def set_logging(cmd_options):
- """
- Setup logging and format output for console and syslog
-
- Syslog is using the KERN facility
- """
- log = logging.getLogger("%s" % (LOG_NAME))
- log_level = logging.INFO
- log_level_console = logging.WARNING
-
- if cmd_options['verbose']:
- log_level_console = logging.INFO
-
- if cmd_options['debug']:
- log_level_console = logging.DEBUG
- log_level = logging.DEBUG
-
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
- sys_formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
-
- console_log = logging.StreamHandler()
- console_log.setLevel(log_level_console)
- console_log.setFormatter(formatter)
-
- syslog_hndlr = SysLogHandler(
- address=cmd_options['syslog'],
- facility=SysLogHandler.LOG_KERN
- )
-
- syslog_hndlr.setFormatter(sys_formatter)
-
- log.setLevel(log_level)
- log.addHandler(console_log)
- log.addHandler(syslog_hndlr)
-
- return log
-
-
-def send_shutdown(pid_file):
- """
- Sends the daemon process a kill signal
- """
- try:
- with open(pid_file, 'r') as pidf:
- pid = int(pidf.readline().strip())
- pidf.close()
- os.kill(pid, 15)
- except:
- log.info("No running instance found!!!")
- log.info("Missing PID file: %s" % (pid_file))
-
-
-def _get_file_age(filename):
- return datetime.datetime.fromtimestamp(
- os.path.getmtime(filename)
- )
-
-
-def monitor_loop(options):
- """
- Checks the options["file"] modification time against an interval. If the
- modification time is too old we terminate the instance.
- """
-
- log.debug("Waiting for init-wait to pass: %s" % (options["init-wait"]))
- time.sleep(options["init-wait"])
-
- log.info("Starting monitor_loop")
-
- terminate_attempts = 0
-
- while options['shutdown'] is False:
- log.debug("In monitor_loop for : %s" % (
- datetime.datetime.now() - options['starttime'])
- )
-
- file_last_modified = _get_file_age(options["file"])
- now = datetime.datetime.now()
-
- delta = now - file_last_modified
- log.debug("File last updated: %s seconds ago" % (delta.seconds))
-
- if delta.seconds > options["dead_time"]:
- log.warning("Dead time exceeded, terminating instance ...")
- # Terminate involves many layers of HTTP / API calls, lots of
- # different errors types could occur here.
- try:
- rksp = Rackspace(options)
- rksp.terminate()
- except Exception as e:
- log.critical("Exception during terminate: %s" % (e))
-
- terminate_attempts += 1
- log.warning("Termination sent, attempt: %s" % (terminate_attempts))
- time.sleep(600)
- else:
- time.sleep(options["check-interval"])
-
- log.info("Leaving monitor_loop")
- log.info("Shutting down")
-
-
-def main():
-
- global log
- global my_daemon
- options = parse_cmd_line(sys.argv)
- log = set_logging(options)
-
- def _shutdown(signalnum=None, frame=None):
- """
- Handles the SIGINT and SIGTERM event, inside of main so it has access to
- the log vars.
- """
-
- log.info("Received shutdown signal")
- options["shutdown"] = True
-
- pid_file = "%s/.gns3dms.pid" % (expanduser("~"))
-
- if options["shutdown"]:
- send_shutdown(pid_file)
- sys.exit(0)
-
- if options["daemon"]:
- my_daemon = MyDaemon(pid_file, options)
-
- # Setup signal to catch Control-C / SIGINT and SIGTERM
- signal.signal(signal.SIGINT, _shutdown)
- signal.signal(signal.SIGTERM, _shutdown)
-
- log.info("Starting ...")
- log.debug("Using settings:")
- for key, value in iter(sorted(options.items())):
- log.debug("%s : %s" % (key, value))
-
- log.debug("Checking file ....")
- if os.path.isfile(options["file"]) is False:
- log.critical("File does not exist!!!")
- sys.exit(1)
-
- test_acess = _get_file_age(options["file"])
- if not isinstance(test_acess, datetime.datetime):
- log.critical("Can't get file modification time!!!")
- sys.exit(1)
-
- if my_daemon:
- my_daemon.start()
- else:
- monitor_loop(options)
-
-
-class MyDaemon(daemon.daemon):
-
- def run(self):
- monitor_loop(self.options)
-
-
-if __name__ == "__main__":
- result = main()
- sys.exit(result)
diff --git a/gns3dms/modules/__init__.py b/gns3dms/modules/__init__.py
deleted file mode 100644
index 0950e877..00000000
--- a/gns3dms/modules/__init__.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-# __version__ is a human-readable version number.
-
-# __version_info__ is a four-tuple for programmatic comparison. The first
-# three numbers are the components of the version number. The fourth
-# is zero for an official release, positive for a development branch,
-# or negative for a release candidate or beta (after the base version
-# number has been incremented)
diff --git a/gns3dms/modules/daemon.py b/gns3dms/modules/daemon.py
deleted file mode 100644
index cfc5539f..00000000
--- a/gns3dms/modules/daemon.py
+++ /dev/null
@@ -1,144 +0,0 @@
-"""Generic linux daemon base class for python 3.x."""
-
-import sys
-import os
-import time
-import atexit
-import signal
-
-
-class daemon:
-
- """A generic daemon class.
-
- Usage: subclass the daemon class and override the run() method."""
-
- def __init__(self, pidfile, options):
- self.pidfile = pidfile
- self.options = options
-
- def daemonize(self):
- """Deamonize class. UNIX double fork mechanism."""
-
- try:
- pid = os.fork()
- if pid > 0:
- # exit first parent
- sys.exit(0)
- except OSError as err:
- sys.stderr.write('fork #1 failed: {0}\n'.format(err))
- sys.exit(1)
-
- # decouple from parent environment
- os.chdir('/')
- os.setsid()
- os.umask(0)
-
- # do second fork
- try:
- pid = os.fork()
- if pid > 0:
-
- # exit from second parent
- sys.exit(0)
- except OSError as err:
- sys.stderr.write('fork #2 failed: {0}\n'.format(err))
- sys.exit(1)
-
- # redirect standard file descriptors
- sys.stdout.flush()
- sys.stderr.flush()
- si = open(os.devnull, 'r')
- so = open(os.devnull, 'a+')
- se = open(os.devnull, 'a+')
-
- os.dup2(si.fileno(), sys.stdin.fileno())
- os.dup2(so.fileno(), sys.stdout.fileno())
- os.dup2(se.fileno(), sys.stderr.fileno())
-
- # write pidfile
- atexit.register(self.delpid)
-
- pid = str(os.getpid())
- with open(self.pidfile, 'w+') as f:
- f.write(pid + '\n')
-
- def delpid(self):
- os.remove(self.pidfile)
-
- def check_pid(self, pid):
- """ Check For the existence of a unix pid. """
- try:
- os.kill(pid, 0)
- except OSError:
- return False
- else:
- return True
-
- def start(self):
- """Start the daemon."""
-
- # Check for a pidfile to see if the daemon already runs
- try:
- with open(self.pidfile, 'r') as pf:
-
- pid = int(pf.read().strip())
- except IOError:
- pid = None
-
- if pid:
- pid_exist = self.check_pid(pid)
-
- if pid_exist:
- message = "Already running: %s\n" % (pid)
- sys.stderr.write(message)
- sys.exit(1)
- else:
- message = "pidfile {0} already exist. " + \
- "but process is dead\n"
- sys.stderr.write(message.format(self.pidfile))
-
- # Start the daemon
- self.daemonize()
- self.run()
-
- def stop(self):
- """Stop the daemon."""
-
- # Get the pid from the pidfile
- try:
- with open(self.pidfile, 'r') as pf:
- pid = int(pf.read().strip())
- except IOError:
- pid = None
-
- if not pid:
- message = "pidfile {0} does not exist. " + \
- "Daemon not running?\n"
- sys.stderr.write(message.format(self.pidfile))
- return # not an error in a restart
-
- # Try killing the daemon process
- try:
- while True:
- os.kill(pid, signal.SIGTERM)
- time.sleep(0.1)
- except OSError as err:
- e = str(err.args)
- if e.find("No such process") > 0:
- if os.path.exists(self.pidfile):
- os.remove(self.pidfile)
- else:
- print(str(err.args))
- sys.exit(1)
-
- def restart(self):
- """Restart the daemon."""
- self.stop()
- self.start()
-
- def run(self):
- """You should override this method when you subclass Daemon.
-
- It will be called after the process has been daemonized by
- start() or restart()."""
diff --git a/gns3dms/modules/rackspace_cloud.py b/gns3dms/modules/rackspace_cloud.py
deleted file mode 100644
index 14c0128f..00000000
--- a/gns3dms/modules/rackspace_cloud.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-# __version__ is a human-readable version number.
-
-# __version_info__ is a four-tuple for programmatic comparison. The first
-# three numbers are the components of the version number. The fourth
-# is zero for an official release, positive for a development branch,
-# or negative for a release candidate or beta (after the base version
-# number has been incremented)
-
-import os
-import sys
-import json
-import logging
-import socket
-
-from gns3dms.cloud.rackspace_ctrl import RackspaceCtrl
-
-
-LOG_NAME = "gns3dms.rksp"
-log = logging.getLogger("%s" % (LOG_NAME))
-
-
-class Rackspace(object):
-
- def __init__(self, options):
- self.username = options["cloud_user_name"]
- self.apikey = options["cloud_api_key"]
- self.authenticated = False
- self.hostname = socket.gethostname()
- self.instance_id = options["instance_id"]
- self.region = options["cloud_region"]
-
- log.debug("Authenticating with Rackspace")
- log.debug("My hostname: %s" % (self.hostname))
- self.rksp = RackspaceCtrl(self.username, self.apikey)
- self.authenticated = self.rksp.authenticate()
-
- def _find_my_instance(self):
- if self.authenticated is not False:
- log.critical("Not authenticated against rackspace!!!!")
-
- for region in self.rksp.list_regions():
- log.debug("Rackspace regions: %s" % (region))
-
- log.debug("Checking region: %s" % (self.region))
- self.rksp.set_region(self.region)
- for server in self.rksp.list_instances():
- log.debug("Checking server: %s" % (server.name))
- if server.id == self.instance_id:
- log.info("Found matching instance: %s" % (server.id))
- log.info("Startup id: %s" % (self.instance_id))
- return server
-
- def terminate(self):
- server = self._find_my_instance()
- log.warning("Sending termination")
- self.rksp.delete_instance(server)
diff --git a/gns3dms/version.py b/gns3dms/version.py
deleted file mode 100644
index 545a0060..00000000
--- a/gns3dms/version.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 GNS3 Technologies Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-# __version__ is a human-readable version number.
-
-# __version_info__ is a four-tuple for programmatic comparison. The first
-# three numbers are the components of the version number. The fourth
-# is zero for an official release, positive for a development branch,
-# or negative for a release candidate or beta (after the base version
-# number has been incremented)
-
-__version__ = "0.1"
-__version_info__ = (0, 0, 1, -99)
diff --git a/requirements.txt b/requirements.txt
index db4f67fd..c12c2071 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,6 @@
netifaces==0.10.4
jsonschema==2.4.0
python-dateutil==2.3
-apache-libcloud==0.16.0
-requests==2.5.0
aiohttp==0.14.4
Jinja2==2.7.3
raven==5.2.0
diff --git a/setup.py b/setup.py
index 4b716076..37fd5902 100644
--- a/setup.py
+++ b/setup.py
@@ -36,8 +36,6 @@ class PyTest(TestCommand):
dependencies = ["aiohttp==0.14.4",
"jsonschema==2.4.0",
- "apache-libcloud==0.16.0",
- "requests==2.5.0",
"Jinja2==2.7.3",
"raven==5.2.0"]