diff --git a/nodepool/molteniron/README.md b/nodepool/molteniron/README.md index d648846..06dded2 100644 --- a/nodepool/molteniron/README.md +++ b/nodepool/molteniron/README.md @@ -1,130 +1,7 @@ -MoltenIron overview -=================== +NOTE: MoltenIron is now an Open Stack project! -MoltenIron maintains a pool of bare metal nodes. +git clone git://git.openstack.org/openstack/molteniron.git +https://git.openstack.org/cgit/openstack/molteniron/ +https://github.com/openstack/molteniron/ -Starting --------- - -Before starting the server for the first time, the createDB.py -script must be run. - -To start the server: -```bash -moltenirond-helper start -``` - -To stop the server: -```bash -moltenirond-helper stop -``` - -MoltenIron client ------------------ - -Use the molteniron client (molteniron) to communicate with the server. For -usage information type: -```bash -molteniron -h -``` - -For usage of a specific command use: -```bash -molteniron [command] -h -``` - -MoltenIron commands -------------------- - -command | description -------- | ----------- -add | Add a node -allocate | Allocate a node -release | Release a node -get_field | Get a specific field in a node -set_field | Set a specific field with a value in a node -status | Return the status of every node -delete_db | Delete every database entry - -Configuration of MoltenIron ---------------------------- - -Configuration of MoltenIron is specified in the file conf.yaml. - -"B)" means that this configuration option is required for both the client and -the server. "C)" means that it is required only for the client. "S)" means -it is only required for the server. - -usage | key | description ------ | --- | ----------- -B) | mi_port | the port that the server uses to respond to commands. -C) | serverIP | The IP address of the server. This is only used by - | | clients. -S) | maxTime | The maximum amount of time, in seconds, that a node - | | is allowed to be allocated to a particular BM node. -S) | logdir | The path to the directory where the logs should be - | | stored. -S) | maxLogDays | The amount of time, in days, to keep old logs. -S) | sqlUser | The username to use for the MI server. This user - | | will automatically be generated when createDB.py is run. -S) | sqlPass | The password of sqlUser - -Running testcases ------------------ - -The suite of testcases is checked by tox. But, before you can run tox, you -need to change the local yaml configuration file to point to the log -directory. - -```bash -(LOG=$(pwd)/testenv/log; sed -i -r -e 's,^(logdir: )(.*)$,\1'${LOG}',' conf.yaml; rm -rf testenv/; tox -e testenv) -``` - -Running inside a Continuous Integration environment ---------------------------------------------------- - -During the creation of a job, add the following snippet of bash code: - -```bash -# Setup MoltenIron and all necessary prerequisites. -# And then call the MI script to allocate a node. -( - REPO_DIR=/opt/stack/new/third-party-ci-tools - MI_CONF_DIR=/usr/local/etc/molteniron/ - MI_IP=10.1.2.3 # @TODO - Replace with your IP addr here! - - # Grab molteniron and install it - git clone https://git.openstack.org/openstack/third-party-ci-tools ${REPO_DIR} || exit 1 - - cd ${REPO_DIR}/nodepool/molteniron - - # @BUG Install prerequisite before running pip to install the requisites - hash mysql_config || sudo apt install -y libmysqlclient-dev - - # Install the requisites for this package - sudo pip install --upgrade --force-reinstall --requirement requirements.txt - - # Run the python package installation program - sudo python setup.py install - - if [ -n "${MI_IP}" ] - then - # Set the molteniron server IP in the conf file - sudo sed -i "s/127.0.0.1/${MI_IP}/g" ${MI_CONF_DIR}/conf.yaml - fi - - export dsvm_uuid - # NOTE: dsvm_uuid used in the following script, hence the -E - sudo -E ${REPO_DIR}/nodepool/molteniron/utils/test_hook_configure_mi.sh -) || exit $? -``` - -and change the MI_IP environment variable to be your MoltenIron server! - -During the destruction of a job, add the following snippet of bash code: - -```bash - DSVM_UUID="$(> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - mi = MoltenIron(conf, unknown_args) - - print(mi.get_response()) - - try: - rc = mi.get_response_map()['status'] - except KeyError: - print("Error: Server returned: %s" % (mi.get_response_map(),)) - rc = 444 - - if rc == 200: - exit(0) - else: - exit(1) diff --git a/nodepool/molteniron/molteniron/moltenirond-helper b/nodepool/molteniron/molteniron/moltenirond-helper deleted file mode 100755 index 0cbc7e3..0000000 --- a/nodepool/molteniron/molteniron/moltenirond-helper +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 sys -import os -import signal -import yaml -from daemonize import Daemonize -from molteniron import moltenirond - -PID = "/var/run/moltenirond.pid" -YAML_CONF = "/usr/local/etc/molteniron/conf.yaml" - -def get_moltenirond_pid(): - if not os.path.isfile(PID): - return -1 - - with open(PID) as fobj: - lines = fobj.readlines() - try: - pid = int(lines[0]) - - try: - # Send harmless kill signal in order to test existance - os.kill(pid, 0) - except Exception: - return -1 - - return pid - except Exception: - return -1 - -def moltenirond_main(): - with open(YAML_CONF, "r") as fobj: - conf = yaml.load(fobj) - - moltenirond.listener(conf) - -if __name__ == "__main__": - - parser = argparse.ArgumentParser(description="Molteniron daemon helper") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - parser.add_argument("-p", - "--pid-dir", - action="store", - type=str, - dest="pid_dir", - help="The directory where PID information is stored") - parser.add_argument("-v", - "--verbose", - action="store", - type=bool, - dest="verbose", - help="Set a verbose information mode") - parser.add_argument("command", type=str, nargs=1, help="the command") - - args = parser.parse_args () - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - YAML_CONF = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - - if args.pid_dir: - if not os.path.isdir (args.pid_dir): - msg = "Error: %s is not a valid directory" % (args.pid_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - PID = os.path.realpath("%s/moltenirond.pid" % (args.pid_dir, )) - - if args.verbose: - print "YAML_CONF = %s" % (YAML_CONF, ) - print "PID = %s" % (PID, ) - - if len(args.command) != 1: - msg = "Error: Expecting one command? Received: %s" % (args.command, ) - print >> sys.stderr, msg - sys.exit (1) - - if args.command[0].upper().lower() == "start": - pid = get_moltenirond_pid() - if pid > 0: - print >> sys.stderr, "Error: The daemon is already running" - sys.exit(1) - daemon = Daemonize(app="moltenirond", - pid=PID, - action=moltenirond_main) - daemon.start() - elif args.command[0].upper().lower() == "stop": - pid = get_moltenirond_pid() - if pid > 0: - os.remove (PID) - os.kill(pid, signal.SIGTERM) - else: - print >> sys.stderr, "Error: The daemon doesn't exist?" - sys.exit(1) - else: - msg = "Error: Unknown command: %s" % (args.command[0], ) - print >> sys.stderr, msg - sys.exit (1) diff --git a/nodepool/molteniron/molteniron/moltenirond.py b/nodepool/molteniron/molteniron/moltenirond.py deleted file mode 100755 index 395470b..0000000 --- a/nodepool/molteniron/molteniron/moltenirond.py +++ /dev/null @@ -1,1105 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler -import calendar -from datetime import datetime -import json -import os -import sys -import time -import traceback -import yaml -import argparse - -from contextlib import contextmanager - -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.sql import insert, update, delete -from sqlalchemy.sql import and_ -from sqlalchemy.types import TIMESTAMP -from sqlalchemy.schema import MetaData, Table - -import sqlalchemy_utils -from sqlalchemy.exc import OperationalError - -DEBUG = False - -metadata = MetaData() - - -class JSON_encoder_with_DateTime(json.JSONEncoder): - def default(self, o): - if isinstance(o, datetime): - return o.isoformat() - - return json.JSONEncoder.default(self, o) - - -# We need to turn BaseHTTPRequestHandler into a "new-style" class for -# Python 2.x -# NOTE: URL is over two lines :( -# http://stackoverflow.com/questions/1713038/super-fails-with-error-typeerror- -# argument-1-must-be-type-not-classobj -class OBaseHTTPRequestHandler(BaseHTTPRequestHandler, object): - pass - -# We need to pass in conf into MoltenIronHandler, so make a class factory -# to do that -# NOTE: URL is over two lines :( -# http://stackoverflow.com/questions/21631799/how-can-i-pass-parameters-to-a- -# requesthandler -def MakeMoltenIronHandlerWithConf(conf): - class MoltenIronHandler(OBaseHTTPRequestHandler): - def __init__(self, *args, **kwargs): - # Note this *needs* to be done before call to super's class! - self.conf = conf - super(OBaseHTTPRequestHandler, self).__init__(*args, **kwargs) - - def do_POST(self): - CL = 'Content-Length' - self.data_string = self.rfile.read(int(self.headers[CL])) - response = self.parse(self.data_string) - self.send_reply(response) - - def send_reply(self, response): - if DEBUG: - print("send_reply: response = %s" % (response,)) - # get the status code off the response json and send it - status_code = response['status'] - self.send_response(status_code) - self.send_header('Content-type', 'application/json') - self.end_headers() - self.wfile.write(json.dumps(response, - cls=JSON_encoder_with_DateTime)) - - def parse(self, request_string): - """Handle the request. Returns the response of the request """ - try: - database = DataBase(self.conf) - # Try to json-ify the request_string - request = json.loads(request_string) - method = request.pop('method') - if method == 'add': - response = database.addBMNode(request) - elif method == 'allocate': - response = database.allocateBM(request['owner_name'], - request['number_of_nodes']) - elif method == 'release': - response = database.deallocateOwner(request['owner_name']) - elif method == 'get_field': - response = database.get_field(request['owner_name'], - request['field_name']) - elif method == 'set_field': - response = database.set_field(request['id'], - request['key'], - request['value']) - elif method == 'status': - response = database.status() - elif method == 'delete_db': - response = database.delete_db() - database.close() - del database - except Exception as e: - response = {'status': 400, 'message': str(e)} - - if DEBUG: - print("parse: response = %s" % (response,)) - - return response - - return MoltenIronHandler - - -class Nodes(declarative_base()): - - __tablename__ = 'Nodes' - - # from sqlalchemy.dialects.mysql import INTEGER - - # CREATE TABLE `Nodes` ( - # id INTEGER NOT NULL AUTO_INCREMENT, #@TODO UNSIGNED - # name VARCHAR(50), - # ipmi_ip VARCHAR(50), - # ipmi_user VARCHAR(50), - # ipmi_password VARCHAR(50), - # port_hwaddr VARCHAR(50), - # cpu_arch VARCHAR(50), - # cpus INTEGER, - # ram_mb INTEGER, - # disk_gb INTEGER, - # status VARCHAR(20), - # provisioned VARCHAR(50), - # timestamp TIMESTAMP NULL, - # PRIMARY KEY (id) - # ) - - id = Column('id', Integer, primary_key=True) - name = Column('name', String(50)) - ipmi_ip = Column('ipmi_ip', String(50)) - ipmi_user = Column('ipmi_user', String(50)) - ipmi_password = Column('ipmi_password', String(50)) - port_hwaddr = Column('port_hwaddr', String(50)) - cpu_arch = Column('cpu_arch', String(50)) - cpus = Column('cpus', Integer) - ram_mb = Column('ram_mb', Integer) - disk_gb = Column('disk_gb', Integer) - status = Column('status', String(20)) - provisioned = Column('provisioned', String(50)) - timestamp = Column('timestamp', TIMESTAMP) - - __table__ = Table(__tablename__, - metadata, - id, - name, - ipmi_ip, - ipmi_user, - ipmi_password, - port_hwaddr, - cpu_arch, - cpus, - ram_mb, - disk_gb, - status, - provisioned, - timestamp) - - def map(self): - return {key: value for key, value - in self.__dict__.items() - if not key.startswith('_') and not callable(key)} - - def __repr__(self): - fmt = """""" - fmt = fmt.replace('\n', ' ') - - return fmt % (self.name, - self.ipmi_ip, - self.ipmi_user, - self.ipmi_password, - self.port_hwaddr, - self.cpu_arch, - self.cpus, - self.ram_mb, - self.disk_gb, - self.status, - self.provisioned, - self.timestamp) - - -class IPs(declarative_base()): - - __tablename__ = 'IPs' - - # CREATE TABLE `IPs` ( - # id INTEGER NOT NULL AUTO_INCREMENT, #@TODO INTEGER(unsigned=True) - # node_id INTEGER, #@TODO UNSIGNED - # ip VARCHAR(50), - # PRIMARY KEY (id), - # FOREIGN KEY(node_id) REFERENCES `Nodes` (id) - # ) - - id = Column('id', - Integer, - primary_key=True) - node_id = Column('node_id', - Integer, - ForeignKey("Nodes.id")) - ip = Column('ip', - String(50)) - - __table__ = Table(__tablename__, - metadata, - id, - node_id, - ip) - - def __repr__(self): - - fmt = """""" - fmt = fmt.replace('\n', ' ') - - return fmt % (self.id, - self.node_id, - self.ip) - -TYPE_MYSQL = 1 -# Is there a mysql memory path? -TYPE_SQLITE = 3 -TYPE_SQLITE_MEMORY = 4 - - -class DataBase(): - """This class may be used access the molten iron database. """ - - def __init__(self, - config, - db_type=TYPE_MYSQL): - self.conf = config - - self.user = self.conf["sqlUser"] - self.passwd = self.conf["sqlPass"] - self.host = "127.0.0.1" - self.database = "MoltenIron" - self.db_type = db_type - - engine = None - try: - # Does the database exist? - engine = self.create_engine() - c = engine.connect() - c.close() - except OperationalError: - sqlalchemy_utils.create_database(engine.url) - engine = self.create_engine() - c = engine.connect() - c.close() - self.engine = engine - - self.create_metadata() - - self.element_info = [ - # The following are returned from the query call - - # field_name length special_fmt skip - ("id", 4, int, False), - ("name", 6, str, False), - ("ipmi_ip", 9, str, False), - ("ipmi_user", 11, str, False), - ("ipmi_password", 15, str, False), - ("port_hwaddr", 19, str, False), - ("cpu_arch", 10, str, False), - ("cpus", 6, int, False), - ("ram_mb", 8, int, False), - ("disk_gb", 9, int, False), - ("status", 8, str, False), - ("provisioned", 13, str, False), - # We add timeString - ("time", 14, float, False), - ] - self.setup_status() - - def create_engine(self): - engine = None - - if self.db_type == TYPE_MYSQL: - engine = create_engine("mysql://%s:%s@%s/%s" - % (self.user, - self.passwd, - self.host, - self.database, ), - echo=DEBUG) - elif self.db_type == TYPE_SQLITE_MEMORY: - engine = create_engine('sqlite:///:memory:', - echo=DEBUG) - elif self.db_type == TYPE_SQLITE: - engine = create_engine("sqlite://%s:%s@%s/%s" - % (self.user, - self.passwd, - self.host, - self.database, ), - echo=DEBUG) - - return engine - - def close(self): - if DEBUG: - print("close: Calling engine.dispose()") - self.engine.dispose() - if DEBUG: - print("close: Finished") - - def get_session(self): - """Get a SQL academy session from the pool """ - Session = sessionmaker(bind=self.engine) - session = Session() - - return session - - def get_connection(self): - """Get a SQL academy connection from the pool """ - conn = self.engine.connect() - - return conn - - @contextmanager - def session_scope(self): - """Provide a transactional scope around a series of operations. """ - session = self.get_session() - try: - yield session - session.commit() - except Exception as e: - if DEBUG: - print("Exception caught in session_scope: %s %s" - % (e, traceback.format_exc(4), )) - session.rollback() - raise - finally: - session.close() - - @contextmanager - def connection_scope(self): - """Provide a transactional scope around a series of operations. """ - conn = self.get_connection() - try: - yield conn - except Exception as e: - if DEBUG: - print("Exception caught in connection_scope: %s" % (e,)) - raise - finally: - conn.close() - - def delete_db(self): - # Instead of: - # IPs.__table__.drop(self.engine, checkfirst=True) - # Nodes.__table__.drop(self.engine, checkfirst=True) - metadata.drop_all(self.engine, checkfirst=True) - - return {'status': 200} - - def create_metadata(self): - # Instead of: - # Nodes.__table__.create(self.engine, checkfirst=True) - # IPs.__table__.create(self.engine, checkfirst=True) - if DEBUG: - print("create_metadata: Calling metadata.create_all") - metadata.create_all(self.engine, checkfirst=True) - if DEBUG: - print("create_metadata: Finished") - - def to_timestamp(self, ts): - timestamp = None - if self.db_type == TYPE_MYSQL: - timestamp = time.strftime("%Y-%m-%d %H:%M:%S", ts) - elif self.db_type in (TYPE_SQLITE, TYPE_SQLITE_MEMORY): - c = calendar.timegm(ts) - timestamp = datetime.fromtimestamp(c) - return timestamp - - def from_timestamp(self, timestamp): - ts = None - if self.db_type == TYPE_MYSQL: - ts = time.strptime(timestamp, "%Y-%m-%d %H:%M:%S") - elif self.db_type == TYPE_SQLITE: - ts = timestamp.timetuple() - return ts - - def allocateBM(self, owner_name, how_many): - """Checkout machines from the database and return necessary info """ - - try: - with self.session_scope() as session, \ - self.connection_scope() as conn: - - # Get a list of IDs for nodes that are free - count = session.query(Nodes).filter_by(status="ready").count() - - # If we don't have enough nodes return an error - if (count < how_many): - fmt = "Not enough available nodes found." - fmt += " Found %d, requested %d" - return {'status': 404, - 'message': fmt % (count, how_many, )} - - nodes_allocated = {} - - for i in range(how_many): - first_ready = session.query(Nodes) - first_ready = first_ready.filter_by(status="ready") - first_ready = first_ready.first() - - id = first_ready.id - # We have everything we need from node - - log(self.conf, - "allocating node id: %d for %s" % (id, owner_name, )) - - timestamp = self.to_timestamp(time.gmtime()) - - # Update the node to the in use state - stmt = update(Nodes) - stmt = stmt.where(Nodes.id == id) - stmt = stmt.values(status="dirty", - provisioned=owner_name, - timestamp=timestamp) - conn.execute(stmt) - - # Refresh the data - session.close() - session = self.get_session() - - first_ready = session.query(Nodes).filter_by(id=id).one() - - first_ready_node = first_ready.map() - - # Query the associated IP table - ips = session.query(IPs).filter_by(node_id=first_ready.id) - - allocation_pool = [] - for ip in ips: - allocation_pool.append(ip.ip) - first_ready_node['allocation_pool'] \ - = ','.join(allocation_pool) - - # Add the node to the nodes dict - nodes_allocated['node_%d' % (id, )] = first_ready_node - - except Exception as e: - - if DEBUG: - print("Exception caught in deallocateBM: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200, 'nodes': nodes_allocated} - - def deallocateBM(self, id): - """Given the ID of a node (or the IPMI IP), de-allocate that node. - - This changes the node status of that node from "used" to "ready." - """ - - try: - with self.session_scope() as session, \ - self.connection_scope() as conn: - - query = session.query(Nodes.id, Nodes.ipmi_ip, Nodes.name) - - if (type(id) == str or type(id) == unicode) and ("." in id): - # If an ipmi_ip was passed - query = query.filter_by(ipmi_ip=id) - else: - query = query.filter_by(id=id) - - node = query.one() - - log(self.conf, - "de-allocating node (%d, %s)" % (node.id, node.ipmi_ip,)) - - stmt = update(Nodes) - stmt = stmt.where(Nodes.id == node.id) - stmt = stmt.values(status="ready", - provisioned="", - timestamp=None) - - conn.execute(stmt) - - except Exception as e: - - if DEBUG: - print("Exception caught in deallocateBM: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200} - - def deallocateOwner(self, owner_name): - """Deallocate all nodes in use by a given BM owner. """ - - try: - with self.session_scope() as session: - nodes = session.query(Nodes.id) - nodes = nodes.filter_by(provisioned=owner_name) - - if nodes.count() == 0: - message = "No nodes are owned by %s" % (owner_name,) - - return {'status': 400, 'message': message} - - for node in nodes: - self.deallocateBM(node.id) - except Exception as e: - if DEBUG: - print("Exception caught in deallocateOwner: %s" % (e,)) - message = "Failed to deallocate node with ID %d" % (node.id,) - return {'status': 400, 'message': message} - - return {'status': 200} - - def addBMNode(self, node): - """Add a new node to molten iron. - - ex: - node = {u'name': u'test', - u'ipmi_user': u'user', - u'port_hwaddr': u'de:ad:be:ef:00:01', - u'disk_gb': 32, - u'cpu_arch': u'ppc64el', - u'ram_mb': 2048, - u'cpus': 8, - u'allocation_pool': u'0.0.0.1,0.0.0.2', - u'ipmi_password': u'password', - u'ipmi_ip': u'0.0.0.0'} - """ - - try: - if DEBUG: - print("addBMNode: node = %s" % (node, )) - - with self.session_scope() as session, \ - self.connection_scope() as conn: - - # Check if it already exists - query = session.query(Nodes) - query = query.filter_by(name=node['name']) - count = query.count() - - if count == 1: - return {'status': 400, 'message': "Node already exists"} - - log(self.conf, - "adding node %(name)s ipmi_ip: %(ipmi_ip)s" % node) - - # Add Node to database - # Note: ID is always 0 as it is an auto-incrementing field - stmt = insert(Nodes) - stmt = stmt.values(name=node['name']) - stmt = stmt.values(ipmi_ip=node['ipmi_ip']) - stmt = stmt.values(ipmi_user=node['ipmi_user']) - stmt = stmt.values(ipmi_password=node['ipmi_password']) - stmt = stmt.values(port_hwaddr=node['port_hwaddr']) - stmt = stmt.values(cpu_arch=node['cpu_arch']) - stmt = stmt.values(cpus=node['cpus']) - stmt = stmt.values(ram_mb=node['ram_mb']) - stmt = stmt.values(disk_gb=node['disk_gb']) - stmt = stmt.values(status='ready') - if 'status' in node: - stmt = stmt.values(status=node['status']) - if 'provisioned' in node: - stmt = stmt.values(provisioned=node['provisioned']) - if 'timestamp' in node: - timestamp_str = node['timestamp'] - if DEBUG: - print("timestamp_str = %s" % (timestamp_str, )) - if len(timestamp_str) != 0 and timestamp_str != "-1": - ts = time.gmtime(float(timestamp_str)) - timestamp = self.to_timestamp(ts) - if DEBUG: - print("timestamp = %s" % (timestamp, )) - stmt = stmt.values(timestamp=timestamp) - if DEBUG: - print(stmt.compile().params) - - conn.execute(stmt) - - # Refresh the data - session.close() - session = self.get_session() - - query = session.query(Nodes).filter_by(name=node['name']) - new_node = query.one() - - # new_node is now a proper Node with an id - - # Add IPs to database - # Note: id is always 0 as it is an auto-incrementing field - ips = node['allocation_pool'].split(',') - for ip in ips: - stmt = insert(IPs) - stmt = stmt.values(node_id=new_node.id, ip=ip) - - if DEBUG: - print(stmt.compile().params) - - conn.execute(stmt) - - except Exception as e: - - if DEBUG: - print("Exception caught in addBMNode: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200} - - def removeBMNode(self, ID, force): - """Remove a node from molten iron - - If force is False it will not remove nodes that are in use. If force - is True then it will always remove the node. - """ - - try: - with self.session_scope() as session, \ - self.connection_scope() as conn: - - query = session.query(Nodes.id, Nodes.ipmi_ip, Nodes.name) - query = query.filter_by(id=int(ID)) - query = query.one() - - log(self.conf, - ("deleting node (id=%d, ipmi_ip=%s, name=%s" - % (query.id, query.ipmi_ip, query.name,))) - - ips = session.query(IPs).filter_by(node_id=int(ID)) - for ip in ips: - stmt = delete(IPs) - stmt = stmt.where(IPs.id == ip.id) - conn.execute(stmt) - - stmt = delete(Nodes) - - if force: - stmt = stmt.where(and_(Nodes.id == query.id, - Nodes.status != "used")) - else: - stmt = stmt.where(Nodes.id == query.id) - - conn.execute(stmt) - - except Exception as e: - - if DEBUG: - print("Exception caught in removeBMNode: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200} - - def cull(self, maxSeconds): - """If any node has been in use for longer than maxSeconds, deallocate - that node. - - Nodes that are deallocated in this way get their state set to "dirty". - They are also scheduled for cleaning. - """ - - if DEBUG: - print("cull: maxSeconds = %s" % (maxSeconds, )) - - nodes_culled = {} - - try: - with self.session_scope() as session: - - nodes = session.query(Nodes) - - if DEBUG: - print("There are %d nodes" % (nodes.count(), )) - - for node in nodes: - - if DEBUG: - print(node) - - if node.timestamp in ('', '-1', None): - continue - - currentTime = self.to_timestamp(time.gmtime()) - elapsedTime = currentTime - node.timestamp - if DEBUG: - print("currentTime = %s" - % (currentTime, )) - print("node.timestamp = %s" - % (node.timestamp, )) - print("elapsedTime = %s" - % (elapsedTime, )) - print("elapsedTime.seconds = %s" - % (elapsedTime.seconds, )) - - if elapsedTime.seconds < int(maxSeconds): - continue - - logstring = ("node %d has been allocated for too long." - % (node.id,)) - log(self.conf, logstring) - - if DEBUG: - print(logstring) - - self.deallocateBM(node.id) - - # Add the node to the nodes dict - nodes_culled['node_%d' % (node.id, )] = node.map() - - except Exception as e: - - if DEBUG: - print("Exception caught in cull: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200, 'nodes': nodes_culled} - - def doClean(self, node_id): - """This function is used to clean a node. """ - - try: - with self.session_scope() as session, \ - self.connection_scope() as conn: - - query = session.query(Nodes) - query = query.filter_by(id=node_id) - node = query.one() - - if node.status in ('ready', ''): - return {'status': 400, - 'message': 'The node at %d has status %s' - % (node.id, node.status,)} - - logstring = "The node at %s has been cleaned." % \ - (node.ipmi_ip,) - log(self.conf, logstring) - - stmt = update(Nodes) - stmt = stmt.where(Nodes.id == node_id) - stmt = stmt.values(status="ready") - - conn.execute(stmt) - - except Exception as e: - - if DEBUG: - print("Exception caught in doClean: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200} - - # @TODO shouldn't it return allocation_pool rather than ipmi_ip? - def get_ips(self, owner_name): - """Return all IPs allocated to a given node owner - - IPs are returned as a list of strings - """ - - ips = [] - - try: - with self.session_scope() as session: - - query = session.query(Nodes) - nodes = query.filter_by(provisioned=owner_name) - - for node in nodes: - ips.append(node.ipmi_ip) - - except Exception as e: - - if DEBUG: - print("Exception caught in get_ips: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200, 'ips': ips} - - def get_field(self, owner_name, field): - """Return entries list with id, field for a given owner, field. """ - - if not hasattr(Nodes, field): - return {'status': 400, - 'message': 'field %s does not exist' % (field,)} - - results = [] - - try: - with self.session_scope() as session: - - query = session.query(Nodes) - nodes = query.filter_by(provisioned=owner_name) - - if DEBUG: - print("There are %d entries provisioned by %s" - % (nodes.count(), owner_name,)) - - if nodes.count() == 0: - return {'status': 404, - 'message': '%s does not own any nodes' - % owner_name} - - for node in nodes: - result = {'id': node.id} - result['field'] = getattr(node, field) - - results.append(result) - - except Exception as e: - - if DEBUG: - print("Exception caught in get_field: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200, 'result': results} - - def set_field(self, id, key, value): - """Given an identifying id, set specified key to the passed value. """ - - if not hasattr(Nodes, key): - return {'status': 400, - 'message': 'field %s does not exist' % (key,)} - - try: - with self.session_scope() as session, \ - self.connection_scope() as conn: - - query = session.query(Nodes) - nodes = query.filter_by(id=id) - - if nodes.count() == 0: - return {'status': 404, - 'message': 'Node with id of %s does not exist!' - % id} - - nodes.one() - - kv = {key: value} - - stmt = update(Nodes) - stmt = stmt.where(Nodes.id == id) - stmt = stmt.values(**kv) - - conn.execute(stmt) - - except Exception as e: - - if DEBUG: - print("Exception caught in set_field: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200} - - def setup_status(self): - """Setup the status formatting strings depending on skipped elements, - lengths, and types. - """ - - self.result_separator = "+" - for (_, length, _, skip) in self.element_info: - if skip: - continue - self.result_separator += '-' * (1 + length + 1) + "+" - - self.description_line = "+" - for (field, length, _, skip) in self.element_info: - if skip: - continue - self.description_line += (" " + - field + - ' ' * (length - len(field)) + - " +") - - index = 0 - self.format_line = "|" - for (_, length, special_fmt, skip) in self.element_info: - if skip: - continue - if special_fmt is int: - self.format_line += " {%d:<%d} |" % (index, length) - elif special_fmt is str: - self.format_line += " {%d:%d} |" % (index, length) - elif special_fmt is float: - self.format_line += " {%d:<%d.%d} |" \ - % (index, length, length - 2) - index += 1 - - def status(self): - """Return a table that details the state of each bare metal node. - - Currently this table is being created manually, there is probably a - better way to be doing this. - """ - - result = "" - - try: - with self.session_scope() as session: - - query = session.query(Nodes) - - result += self.result_separator + "\n" - result += self.description_line + "\n" - result += self.result_separator + "\n" - - for node in query: - - timeString = "" - try: - if node.timestamp is not None: - elapsedTime = datetime.utcnow() - node.timestamp - timeString = str(elapsedTime) - except Exception: - pass - - elements = (node.id, - node.name, - node.ipmi_ip, - node.ipmi_user, - node.ipmi_password, - node.port_hwaddr, - node.cpu_arch, - node.cpus, - node.ram_mb, - node.disk_gb, - node.status, - node.provisioned, - timeString) - - new_elements = [] - index = 0 - for (_, _, _, skip) in self.element_info: - if not skip: - new_elements.append(elements[index]) - index += 1 - - result += self.format_line.format(*new_elements) + "\n" - - result += self.result_separator + "\n" - - except Exception as e: - - if DEBUG: - print("Exception caught in status: %s" % (e,)) - - # Don't send the exception object as it is not json serializable! - return {'status': 400, 'message': str(e)} - - return {'status': 200, 'result': result} - - -def listener(conf): - mi_addr = str(conf['serverIP']) - mi_port = int(conf['mi_port']) - handler_class = MakeMoltenIronHandlerWithConf(conf) - print('Listening... to %s:%d' % (mi_addr, mi_port,)) - moltenirond = HTTPServer((mi_addr, mi_port), handler_class) - moltenirond.serve_forever() - - -def cleanup(): - """This function kills any running instances of molten iron. - - This should be called when starting a new instance of molten iron. - """ - ps = os.popen("ps aux | grep python | grep moltenIronD.py").read() - processes = ps.split("\n") - pids = [] - for process in processes: - if "grep" in process: - continue - words = process.split(" ") - actual = [] - for word in words: - if word != "": - actual += [word] - words = actual - if len(words) > 1: - pids += [words[1]] - myPID = os.getpid() - - for pid in pids: - if int(pid) == int(myPID): - continue - os.system("kill -9 " + pid) - - -def log(conf, message): - """Write a message to the log file. """ - cleanLogs(conf) - logdir = conf["logdir"] - now = datetime.today() - - fname = str(now.day) + "-" + str(now.month) \ - + "-" + str(now.year) + ".log" - - timestamp = "{0:0>2}".format(str(now.hour)) + ":" + \ - "{0:0>2}".format(str(now.minute)) \ - + ":" + "{0:0>2}".format(str(now.second)) - - message = timestamp + " " + message + "\n" - - # check if logdir exists, if not create it - if not os.path.isdir(logdir): - os.popen("mkdir " + logdir) - - fobj = open(logdir + "/" + fname, "a") - fobj.write(message) - fobj.close() - - -def cleanLogs(conf): - """Find and delete log files that have been around for too long. """ - logdir = conf["logdir"] - maxDays = conf["maxLogDays"] - if not os.path.isdir(logdir): - return - now = datetime.today() - logs = os.popen("ls " + logdir).read().split("\n") - for log in logs: - elements = log[:-1 * len(".log")].split("-") - if len(elements) != 3: - continue - newDate = datetime(int(elements[2]), - int(elements[1]), - int(elements[0])) - if (now - newDate).days > maxDays: - os.popen("rm " + logdir + "/" + log) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron daemon") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args () - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - listener(conf) diff --git a/nodepool/molteniron/requirements.txt b/nodepool/molteniron/requirements.txt deleted file mode 100644 index 4a94692..0000000 --- a/nodepool/molteniron/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -daemonize -MySQL-python -pyyaml -sqlalchemy -sqlalchemy_utils diff --git a/nodepool/molteniron/setup.py b/nodepool/molteniron/setup.py deleted file mode 100755 index 849d787..0000000 --- a/nodepool/molteniron/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 distutils.core import setup - -setup(name="molteniron", - version="1.0", - description="database for Ironic Baremetal services", - url="https://github.com/openstack/third-party-ci-tools", - py_modules=["molteniron/__init__", "molteniron/moltenirond"], - scripts=["molteniron/moltenirond-helper", "molteniron/molteniron"], - data_files=[("etc/molteniron/", ["conf.yaml"])] - ) diff --git a/nodepool/molteniron/tests/testAddBMNode.py b/nodepool/molteniron/tests/testAddBMNode.py deleted file mode 100755 index e7a29ea..0000000 --- a/nodepool/molteniron/tests/testAddBMNode.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "2f024600fc5ef6f7", - "port_hwaddr": "97:c3:b0:47:0c:0d", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "6cf0957c985b2deb", - "port_hwaddr": "2d:9e:3c:83:8a:be", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "cc777c10196db585", - "port_hwaddr": "47:b0:dc:d5:82:d9", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "ipmi_password": "a700a2d789075276", - "port_hwaddr": "44:94:1a:c7:8a:9f", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "6b8823ef-3e14-4811-98b9-32e27397540d", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node1) - print ret - assert ret['status'] == 400 - assert ret['message'] == "Node already exists" - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret['status'] == 400 - assert ret['message'] == "Node already exists" - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret['status'] == 400 - assert ret['message'] == "Node already exists" - - database.close() - del database diff --git a/nodepool/molteniron/tests/testAllocateBM.py b/nodepool/molteniron/tests/testAllocateBM.py deleted file mode 100755 index 337aff1..0000000 --- a/nodepool/molteniron/tests/testAllocateBM.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -def compare_provisioned_nodes(lhs, rhs): - lhs = lhs.copy() - rhs = rhs.copy() - rhs['provisioned'] = 'hamzy' - del lhs['status'] - del lhs['timestamp'] - del rhs['status'] - del rhs['timestamp'] - del lhs['id'] - assert lhs == rhs - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "e05cc5f061426e34", - "port_hwaddr": "f8:de:29:33:a4:ed", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "0614d63b6635ea3d", - "port_hwaddr": "4c:c5:da:28:2c:2d", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "928b056134e4d770", - "port_hwaddr": "53:76:c6:09:50:64", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "ipmi_password": "33f448a4fc176492", - "port_hwaddr": "85:e0:73:e9:fc:ca", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "6b8823ef-3e14-4811-98b9-32e27397540d", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.allocateBM("hamzy", 1) - print ret - assert ret['status'] == 200 - assert len(ret["nodes"]) == 1 - compare_provisioned_nodes (ret["nodes"]["node_1"], node1) - - database.close() - del database - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - database.delete_db() - database.close() - del database - - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.allocateBM("hamzy", 2) - print ret - assert ret['status'] == 200 - assert len(ret["nodes"]) == 2 - compare_provisioned_nodes (ret["nodes"]["node_1"], node1) - compare_provisioned_nodes (ret["nodes"]["node_2"], node2) - - database.close() - del database - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - database.delete_db() - database.close() - del database - - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.allocateBM("hamzy", 3) - print ret - assert ret == {'status': 404, - 'message': ('Not enough available nodes found. ' - 'Found 2, requested 3')} - - database.close() - del database diff --git a/nodepool/molteniron/tests/testCull.py b/nodepool/molteniron/tests/testCull.py deleted file mode 100755 index 70aa9dc..0000000 --- a/nodepool/molteniron/tests/testCull.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -import time -from molteniron import moltenirond - -def compare_culled_nodes(lhs, rhs): - lhs = lhs.copy() - rhs = rhs.copy() - del rhs['allocation_pool'] - # timestamp can be converted on the server - del lhs['timestamp'] - del rhs['timestamp'] - del lhs['id'] - assert lhs == rhs - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "34118fd3509621ba", - "port_hwaddr": "ff:2c:e1:cc:8e:7c", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "fa2125690a95b43c", - "port_hwaddr": "f6:58:13:02:64:59", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "-1", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "3aee014d56425a6c", - "port_hwaddr": "6e:d4:a5:ae:13:55", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - # NOTE: time() can return fractional values. Ex: 1460560140.47 - "timestamp": str (time.time() - 1000.0), - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "ipmi_password": "254dd9fb34ddcac7", - "port_hwaddr": "a0:c9:22:23:22:9d", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "6b8823ef-3e14-4811-98b9-32e27397540d", - "timestamp": str (time.time() - 2000.0), - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.cull (1000) - print ret - assert ret['status'] == 200 - assert len(ret['nodes']) == 2 - - compare_culled_nodes (ret['nodes']['node_3'], node3) - compare_culled_nodes (ret['nodes']['node_4'], node4) - - database.close() - del database - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - database.delete_db() - database.close() - del database - - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.cull (2000) - print ret - assert ret['status'] == 200 - assert len(ret['nodes']) == 1 - - compare_culled_nodes (ret['nodes']['node_4'], node4) - - database.close() - del database - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - database.delete_db() - database.close() - del database - - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.cull (3000) - print ret - assert ret['status'] == 200 - assert len(ret['nodes']) == 0 - - database.close() - del database diff --git a/nodepool/molteniron/tests/testDeallocateBM.py b/nodepool/molteniron/tests/testDeallocateBM.py deleted file mode 100755 index 8202b62..0000000 --- a/nodepool/molteniron/tests/testDeallocateBM.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -def compare_provisioned_nodes(lhs, rhs): - lhs = lhs.copy() - rhs = rhs.copy() - rhs['provisioned'] = 'hamzy' - del lhs['status'] - del lhs['timestamp'] - del rhs['status'] - del rhs['timestamp'] - del lhs['id'] - assert lhs == rhs - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "16f6954d347c4de2", - "port_hwaddr": "28:5a:e7:e3:fe:75", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "3a23241cfa516699", - "port_hwaddr": "7d:0a:e5:b9:41:9b", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "4f7e47c57f27ec55", - "port_hwaddr": "5a:e8:11:e9:11:a2", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "ipmi_password": "aeff165ded2c2f9f", - "port_hwaddr": "4d:18:82:dc:2c:d6", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "6b8823ef-3e14-4811-98b9-32e27397540d", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.allocateBM("hamzy", 1) - print ret - assert ret['status'] == 200 - assert len(ret["nodes"]) == 1 - compare_provisioned_nodes (ret["nodes"]["node_1"], node1) - - session = database.get_session() - n1 = session.query(moltenirond.Nodes).filter_by(name=node1["name"]).one() - session.close() - ret = database.deallocateBM(n1.id) - print ret - assert ret['status'] == 200 - - database.close() - del database - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - database.delete_db() - database.close() - del database - - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.allocateBM("hamzy", 1) - print ret - assert ret['status'] == 200 - assert len(ret["nodes"]) == 1 - compare_provisioned_nodes (ret["nodes"]["node_1"], node1) - - session = database.get_session() - n1 = session.query(moltenirond.Nodes).filter_by(name=node1["name"]).one() - session.close() - ret = database.deallocateBM(n1.ipmi_ip) - print ret - assert ret['status'] == 200 - - database.close() - del database diff --git a/nodepool/molteniron/tests/testDeallocateOwner.py b/nodepool/molteniron/tests/testDeallocateOwner.py deleted file mode 100755 index dd7e0df..0000000 --- a/nodepool/molteniron/tests/testDeallocateOwner.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -def compare_provisioned_nodes(lhs, rhs): - lhs = lhs.copy() - rhs = rhs.copy() - rhs['provisioned'] = 'hamzy' - del lhs['status'] - del lhs['timestamp'] - del rhs['status'] - del rhs['timestamp'] - del lhs['id'] - assert lhs == rhs - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "f367d07be07d6358", - "port_hwaddr": "6d:9a:78:f3:ed:3a", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "1c6a27307f8fe79d", - "port_hwaddr": "16:23:e8:07:b4:a9", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "1766d597a024dd8d", - "port_hwaddr": "12:33:9f:04:07:9b", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "ipmi_password": "7c55be8b4ef42869", - "port_hwaddr": "c2:31:e9:8a:75:96", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "6b8823ef-3e14-4811-98b9-32e27397540d", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.allocateBM("hamzy", 1) - print ret - assert ret['status'] == 200 - assert len(ret["nodes"]) == 1 - compare_provisioned_nodes (ret["nodes"]["node_1"], node1) - - ret = database.deallocateOwner("hamzy") - print ret - assert ret['status'] == 200 - - database.close() - del database diff --git a/nodepool/molteniron/tests/testDoClean.py b/nodepool/molteniron/tests/testDoClean.py deleted file mode 100755 index 8e9c676..0000000 --- a/nodepool/molteniron/tests/testDoClean.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "e23af1e52896cf02", - "port_hwaddr": "5d:7e:05:dd:fe:65", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "57212373db56c76a", - "port_hwaddr": "7e:41:89:e1:28:03", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "c2f4b0bfa31fe9de", - "port_hwaddr": "4f:a7:48:59:6a:a7", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "ipmi_password": "f99d122fc129c1dd", - "port_hwaddr": "a2:0d:bc:ca:c5:a5", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "6b8823ef-3e14-4811-98b9-32e27397540d", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.doClean(1) - print ret - assert ret == {'status': 400, 'message': 'The node at 1 has status ready'} - - ret = database.doClean(2) - print ret - assert ret == {'status': 400, 'message': 'The node at 2 has status ready'} - - ret = database.doClean(3) - print ret - assert ret == {'status': 200} - - ret = database.doClean(4) - print ret - assert ret == {'status': 200} diff --git a/nodepool/molteniron/tests/testGetField.py b/nodepool/molteniron/tests/testGetField.py deleted file mode 100755 index 7813b74..0000000 --- a/nodepool/molteniron/tests/testGetField.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "7db1486ac9ea6533", - "port_hwaddr": "ca:2c:ab:88:47:b0", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "hamzy", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "c3f8e3f3407e880b", - "port_hwaddr": "90:24:5c:d5:0e:b3", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "mjturek", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "1bcbf739c7108291", - "port_hwaddr": "9c:6b:1b:31:a5:2d", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "cpu_arch": "ppc64el", - "ipmi_password": "0c200c858ac46280", - "port_hwaddr": "64:49:12:c6:3f:bd", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "mjturek", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.get_field("hamzy", "cpus") - print ret - assert ret['status'] == 200 - assert len(ret['result']) ==1 - assert ret['result'][0]['field'] == node1["cpus"] - - ret = database.get_field("mjturek", "port_hwaddr") - print ret - assert ret['status'] == 200 - assert len(ret['result']) == 2 - assert ret['result'][0]['field'] == node2["port_hwaddr"] - assert ret['result'][1]['field'] == node4["port_hwaddr"] - - ret = database.get_field("mmedvede", "candy") - print ret - assert ret == {'status': 400, 'message': 'field candy does not exist'} - - database.close() - del database diff --git a/nodepool/molteniron/tests/testGetIps.py b/nodepool/molteniron/tests/testGetIps.py deleted file mode 100755 index 2af8fd5..0000000 --- a/nodepool/molteniron/tests/testGetIps.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "1aa328d7767653ad", - "port_hwaddr": "17:e7:f1:ab:a5:9f", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "hamzy", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "84b9d9ceb866f612", - "port_hwaddr": "0b:f1:9c:9d:a6:eb", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "mjturek", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "ba60285a1fd69800", - "port_hwaddr": "da:e0:86:2a:80:9c", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "cpu_arch": "ppc64el", - "ipmi_password": "7810c66057ef4f2d", - "port_hwaddr": "d6:bc:ca:83:95:e7", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "mjturek", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - ret = database.get_ips("hamzy") - print ret - assert ret['status'] == 200 - assert len(ret['ips']) == 1 - assert ret['ips'] == [ node1["ipmi_ip"] ] - - ret = database.get_ips("mjturek") - print ret - assert ret['status'] == 200 - assert len(ret['ips']) == 2 - assert ret['ips'] == [ node2["ipmi_ip"], node4["ipmi_ip"] ] - - database.close() - del database diff --git a/nodepool/molteniron/tests/testRemoveBMNode.py b/nodepool/molteniron/tests/testRemoveBMNode.py deleted file mode 100755 index bc8a778..0000000 --- a/nodepool/molteniron/tests/testRemoveBMNode.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 IBM Corporation. -# -# 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 -import os -import yaml -import argparse -from molteniron import moltenirond - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Molteniron command line tool") - parser.add_argument("-c", - "--conf-dir", - action="store", - type=str, - dest="conf_dir", - help="The directory where configuration is stored") - - args = parser.parse_args(sys.argv[1:]) - - if args.conf_dir: - if not os.path.isdir (args.conf_dir): - msg = "Error: %s is not a valid directory" % (args.conf_dir, ) - print >> sys.stderr, msg - sys.exit(1) - - yaml_file = os.path.realpath("%s/conf.yaml" % (args.conf_dir, )) - else: - yaml_file = "/usr/local/etc/molteniron/conf.yaml" - - with open(yaml_file, "r") as fobj: - conf = yaml.load(fobj) - - node1 = { - "name": "pkvmci816", - "ipmi_ip": "10.228.219.134", - "ipmi_user": "user", - "ipmi_password": "2703f5fee17f2073", - "port_hwaddr": "b1:71:dd:02:9e:20", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.10,10.228.112.11" - } - node2 = { - "name": "pkvmci818", - "ipmi_ip": "10.228.219.133", - "ipmi_user": "user", - "ipmi_password": "c3f06ff4b798a4ea", - "port_hwaddr": "88:6e:9e:fa:65:d8", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "ready", - "provisioned": "", - "timestamp": "", - "allocation_pool": "10.228.112.8,10.228.112.9" - } - node3 = { - "name": "pkvmci851", - "ipmi_ip": "10.228.118.129", - "ipmi_user": "user", - "ipmi_password": "2885d1af50781461", - "port_hwaddr": "a2:a2:64:79:6b:69", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "7a72eccd-3153-4d08-9848-c6d3b1f18f9f", - "timestamp": "1460489832", - "allocation_pool": "10.228.112.12,10.228.112.13" - } - node4 = { - "name": "pkvmci853", - "ipmi_ip": "10.228.118.133", - "ipmi_user": "user", - "ipmi_password": "3e374dc88ca43b4f", - "port_hwaddr": "50:4a:56:3c:e9:0f", - "cpu_arch": "ppc64el", - "cpus": 20L, - "ram_mb": 51000L, - "disk_gb": 500L, - "status": "used", - "provisioned": "6b8823ef-3e14-4811-98b9-32e27397540d", - "timestamp": "1460491566", - "allocation_pool": "10.228.112.14,10.228.112.15" - } - - # 8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<-----8<----- - database = moltenirond.DataBase(conf, moltenirond.TYPE_SQLITE_MEMORY) - ret = database.addBMNode (node1) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node2) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node3) - print ret - assert ret == {'status': 200} - ret = database.addBMNode (node4) - print ret - assert ret == {'status': 200} - - session = database.get_session() - n1 = session.query(moltenirond.Nodes).filter_by(name=node1["name"]).one() - session.close() - ret = database.removeBMNode(n1.id, False) - print ret - assert ret['status'] == 200 - - database.close() - del database diff --git a/nodepool/molteniron/tox.ini b/nodepool/molteniron/tox.ini deleted file mode 100644 index 5a54ac7..0000000 --- a/nodepool/molteniron/tox.ini +++ /dev/null @@ -1,83 +0,0 @@ -[tox] -envlist = py27 - -[testenv:devenv] -envdir = devenv -basepython = python2.7 -# usedevelop = True -# will create a devenv/lib/python2.7/site-packages/molteniron.egg-link which -# will point back to the git directory. -# Instead, we want the module installed in the virtual environment. -usedevelop = False -deps = -rrequirements.txt - -[testenv:testenv] -envdir = testenv -basepython = python2.7 -# usedevelop = True -# will create a testenv/lib/python2.7/site-packages/molteniron.egg-link which -# will point back to the git directory. -# Instead, we want the module installed in the virtual environment. -usedevelop = False -# Skip automatic tarballing of source distribution. We will manually run -# setup.py later... -skipsdist = True -# Don't worry about installing bash commands in the virtual environment. -whitelist_externals = mkdir -deps = -rrequirements.txt -commands = mkdir -p testenv/var/run/ - python setup.py \ - install \ - --install-data=testenv/ \ - --install-scripts=testenv/bin/ \ - --install-purelib=testenv/lib/python2.7/site-packages/ - moltenirond-helper \ - --conf-dir=testenv/etc/molteniron/ \ - --pid-dir=testenv/var/run/ \ - start - molteniron \ - --conf-dir=testenv/etc/molteniron/ \ - delete_db - molteniron \ - --conf-dir=testenv/etc/molteniron/ \ - add test 10.1.2.1 user password 10.1.2.3,10.1.2.4 de:ad:be:ef:00:01 ppc64el 8 2048 32 - molteniron \ - --conf-dir=testenv/etc/molteniron/ \ - allocate hamzy 1 - molteniron \ - --conf-dir=testenv/etc/molteniron/ \ - get_field hamzy port_hwaddr - molteniron \ - --conf-dir=testenv/etc/molteniron/ \ - release hamzy - python \ - tests/testAllocateBM.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testAddBMNode.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testCull.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testDeallocateBM.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testDeallocateOwner.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testDoClean.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testGetField.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testGetIps.py \ - --conf-dir=testenv/etc/molteniron/ - python \ - tests/testRemoveBMNode.py \ - --conf-dir=testenv/etc/molteniron/ - moltenirond-helper \ - --conf-dir=testenv/etc/molteniron/ \ - --pid-dir=testenv/var/run/ \ - stop diff --git a/nodepool/molteniron/utils/test_hook_configure_mi.sh b/nodepool/molteniron/utils/test_hook_configure_mi.sh deleted file mode 100755 index 7c9f289..0000000 --- a/nodepool/molteniron/utils/test_hook_configure_mi.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2016 IBM Corporation. -# -# 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. - -errcho() -{ - # Redirect stdout to stderr for the echo command - >&2 echo $@; -} - -# -# NOTE: -# -# This script expects the environment variable dsvm_uuid to be set! -# - -if [ -z "${dsvm_uuid}" ] -then - errcho "Error: environment variable unset: dsvm_uuid" - exit 1 -fi - -# Is the command-line JSON processor installed? -hash jq || sudo apt-get install -y jq - -# Turn off Bash debugging temporarily to stop password being shown in log files -set +x - -# allocate a BM node to a dsvm guest named dsvm_uuid, then amend the localrc -# and hardware_info files. -JSON_RSP=$(molteniron allocate $dsvm_uuid 1) - -if [ $? -gt 0 ] -then - # Save the response for local debugging - echo "${JSON_RSP}" > /tmp/json.rsp - - errcho "Error: allocate $dsvm_uuid 1" - - MESSAGE=$(echo "${JSON_RSP}" | jq .message) - - # Is there a message response? - # NOTE: jq not finding a message key returns null - if [ $? -eq 0 -a "${MESSAGE}" != "null" ] - then - errcho "Error: ${MESSAGE}" - fi - - exit 1 -fi - -# Convert from a JSON string into a Bash array -declare -A NODE -while IFS="=" read -r key value -do - NODE[$key]="$value" -done < <( - echo ${JSON_RSP} | jq --raw-output '.nodes[]|to_entries|map("\(.key)=\(.value|tostring)")|.[]' - RC=$? - if [ ${RC} -gt 0 ] - then - echo "error=${RC}" - fi -) - -if [ -n "${NODE[error]}" ] -then - errcho "Error: jq failed to parse response" - errcho "jq .nodes:" - errcho ${JSON_RSP} | jq '.nodes[]' - errcho "jq .nodes|to_entries|map:" - errcho ${JSON_RSP} | jq --raw-output '.nodes[]|to_entries|map("\(.key)=\(.value|tostring)")|.[]' - exit 2 -elif [ -z "${NODE[ipmi_ip]}" \ - -o -z "${NODE[port_hwaddr]}" \ - -o -z "${NODE[ipmi_user]}" \ - -o -z "${NODE[ipmi_password]}" ] -then - echo "ERROR: One of NODE's ipmi_ip, port_hwaddr, ipmi_user, or ipmi_password is empty!" - if [ -n "${NODE[ipmi_password]}" ] - then - SAFE_PASSWORD="*hidden*" - else - SAFE_PASSWORD="" - fi - echo "NODE[ipmi_ip] = ${NODE[ipmi_ip]}" - echo "NODE[port_hwaddr] = ${NODE[port_hwaddr]}" - echo "NODE[ipmi_user] = ${NODE[ipmi_user]}" - echo "NODE[ipmi_password] = ${SAFE_PASSWORD}" - echo "jq command returns:" - echo ${JSON_RSP} | jq --raw-output '.nodes[]|to_entries|map("\(.key)=\(.value|tostring)")|.[]' - exit 3 -fi - -# Set IPMI info file -printf "${NODE[ipmi_ip]} ${NODE[port_hwaddr]} ${NODE[ipmi_user]} ${NODE[ipmi_password]}\\n" > "/opt/stack/new/devstack/files/hardware_info" - -set -x - -# Add the hardware properties to the localrc file -printf "IRONIC_HW_ARCH=${NODE[cpu_arch]}\\nIRONIC_HW_NODE_CPU=${NODE[cpus]}\\nIRONIC_HW_NODE_RAM=${NODE[ram_mb]}\\nIRONIC_HW_NODE_DISK=${NODE[disk_gb]}\\n" >> "/opt/stack/new/devstack/localrc" - -# Add the allocation pools to the localrc -IFS=',' read -r -a ALLOCATION_POOL <<< ${NODE[allocation_pool]} - -POOL='' -for IP in ${ALLOCATION_POOL[@]} -do - echo "IP=${IP}" - if [ -n "${POOL}" ] - then - POOL+=" --allocation-pool " - fi - POOL+="start=${IP},end=${IP}" -done -# append ip pools to the end of our localrc -printf "ALLOCATION_POOL=\"${POOL}\"\n" >> "/opt/stack/new/devstack/localrc"