create_release fix and provisioning added
This commit is contained in:
parent
ba3fcea38b
commit
a64d94c90f
|
@ -14,6 +14,7 @@
|
|||
|
||||
/build
|
||||
nosetests.xml
|
||||
nailgun.log
|
||||
lock
|
||||
|
||||
.idea
|
||||
|
|
|
@ -41,101 +41,83 @@ def main():
|
|||
|
||||
logger.info("=== Creating release ===")
|
||||
with open(params.release_file, "r") as f:
|
||||
logger.debug("Trying to parse release file")
|
||||
data = json.load(f)
|
||||
logger.debug("Trying to parse release file")
|
||||
data = json.load(f)
|
||||
|
||||
httpconn = httplib.HTTPConnection(host=params.host, port=params.port)
|
||||
httpheaders = {"Content-Type": "application/json"}
|
||||
|
||||
httpdata = {}
|
||||
for field in ('name', 'version', 'description', 'networks_metadata'):
|
||||
httpdata[field] = data[field]
|
||||
httpdata[field] = data[field]
|
||||
|
||||
logger.info("Request url: %s, data: %s" % \
|
||||
('/api/releases', json.dumps(httpdata)))
|
||||
|
||||
if params.dry_run:
|
||||
release_dict = {'id': '1'}
|
||||
release_dict = {'id': '1'}
|
||||
else:
|
||||
logger.debug("Sending request")
|
||||
try:
|
||||
httpconn.request(method="POST",
|
||||
url="/api/releases",
|
||||
body=json.dumps(httpdata),
|
||||
headers=httpheaders)
|
||||
except Exception as e:
|
||||
logger.error("Error: %s" % str(e))
|
||||
sys.exit(1)
|
||||
else:
|
||||
response = httpconn.getresponse()
|
||||
response_body = response.read()
|
||||
logger.debug("Response status: %s" % response.status)
|
||||
logger.debug("Response body: %s" % response_body)
|
||||
release_dict = {}
|
||||
logger.debug("Sending request")
|
||||
try:
|
||||
httpconn.request(method="POST",
|
||||
url="/api/releases",
|
||||
body=json.dumps(httpdata),
|
||||
headers=httpheaders)
|
||||
except Exception as e:
|
||||
logger.error("Error: %s" % str(e))
|
||||
sys.exit(1)
|
||||
else:
|
||||
response = httpconn.getresponse()
|
||||
response_body = response.read()
|
||||
logger.debug("Response status: %s" % response.status)
|
||||
logger.debug("Response body: %s" % response_body)
|
||||
|
||||
if response.status == 200:
|
||||
logger.info("Release '%s' has been successfully added" % \
|
||||
httpdata['name'])
|
||||
release_dict = json.loads(response_body)
|
||||
logger.info("Release id: %s" % release_dict['id'])
|
||||
elif response.status == 409:
|
||||
logger.error("Release '%s' already exists" % httpdata['name'])
|
||||
sys.exit(0)
|
||||
elif response.status < 200 and response.status >= 300:
|
||||
logger.error("Error in creating release: %s" % response.read())
|
||||
sys.exit(1)
|
||||
if response.status == 201:
|
||||
logger.info("Release '%s' has been successfully added" % \
|
||||
httpdata['name'])
|
||||
release_dict = json.loads(response_body)
|
||||
logger.info("Release id: %s" % release_dict['id'])
|
||||
elif response.status == 409:
|
||||
logger.error("Release '%s' already exists" % httpdata['name'])
|
||||
sys.exit(0)
|
||||
elif response.status < 201 and response.status >= 300:
|
||||
logger.error("Error in creating release: %s" % response.read())
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def add_item(httpconn, handler_url, release,
|
||||
item_collection, item_fields):
|
||||
|
||||
for item in item_collection:
|
||||
httpdata = {}
|
||||
for field in item_fields:
|
||||
httpdata[field] = item[field]
|
||||
for item in item_collection:
|
||||
httpdata = {}
|
||||
for field in item_fields:
|
||||
httpdata[field] = item[field]
|
||||
|
||||
if params.dry_run:
|
||||
httpdata['release'] = '1'
|
||||
else:
|
||||
httpdata['release'] = release
|
||||
|
||||
logger.debug("Request: url: %s, data: %s" % \
|
||||
(handler_url, json.dumps(httpdata)))
|
||||
if params.dry_run:
|
||||
httpdata['release'] = '1'
|
||||
else:
|
||||
httpdata['release'] = release
|
||||
|
||||
logger.debug("Request: url: %s, data: %s" % \
|
||||
(handler_url, json.dumps(httpdata)))
|
||||
|
||||
if not params.dry_run:
|
||||
logger.debug("Sending request")
|
||||
try:
|
||||
httpconn.request(method="POST",
|
||||
url=handler_url,
|
||||
body=json.dumps(httpdata),
|
||||
headers=httpheaders)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Error: %s" % str(e))
|
||||
raise e
|
||||
else:
|
||||
response = httpconn.getresponse()
|
||||
response_body = response.read()
|
||||
logger.debug("Response status: %s" % response.status)
|
||||
logger.debug("Response body: %s" % response_body)
|
||||
|
||||
|
||||
add_item(
|
||||
httpconn, '/api/points', release_dict['id'],
|
||||
data['points'],
|
||||
('name', 'scheme')
|
||||
)
|
||||
|
||||
add_item(
|
||||
httpconn, '/api/coms', release_dict['id'],
|
||||
data['components'],
|
||||
('name', 'deploy', 'provides', 'requires')
|
||||
)
|
||||
|
||||
add_item(
|
||||
httpconn, '/api/roles', release_dict['id'],
|
||||
data['roles'],
|
||||
('name', 'components')
|
||||
)
|
||||
if not params.dry_run:
|
||||
logger.debug("Sending request")
|
||||
try:
|
||||
httpconn.request(method="POST",
|
||||
url=handler_url,
|
||||
body=json.dumps(httpdata),
|
||||
headers=httpheaders)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Error: %s" % str(e))
|
||||
raise e
|
||||
else:
|
||||
response = httpconn.getresponse()
|
||||
response_body = response.read()
|
||||
logger.debug("Response status: %s" % response.status)
|
||||
logger.debug("Response body: %s" % response_body)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import logging
|
||||
import itertools
|
||||
|
||||
import web
|
||||
import ipaddr
|
||||
import netaddr
|
||||
|
||||
from models import Release, Cluster, Node, Role, Network, Vlan
|
||||
from settings import settings
|
||||
import rpc
|
||||
from settings import settings
|
||||
from provision import ProvisionConfig
|
||||
from provision import ProvisionFactory
|
||||
from provision.model.profile import Profile as ProvisionProfile
|
||||
from provision.model.node import Node as ProvisionNode
|
||||
from provision.model.power import Power as ProvisionPower
|
||||
from models import Release, Cluster, Node, Role, Network, Vlan
|
||||
|
||||
|
||||
def check_client_content_type(handler):
|
||||
|
@ -229,6 +237,43 @@ class ClusterChangesHandler(JSONHandler):
|
|||
if not cluster:
|
||||
return web.notfound()
|
||||
|
||||
pc = ProvisionConfig()
|
||||
pc.cn = "provision.driver.cobbler.Cobbler"
|
||||
pc.url = settings.COBBLER_URL
|
||||
pc.user = settings.COBBLER_USER
|
||||
pc.password = settings.COBBLER_PASSWORD
|
||||
try:
|
||||
pd = ProvisionFactory.getInstance(pc)
|
||||
except:
|
||||
raise web.badrequest()
|
||||
pf = ProvisionProfile(settings.COBBLER_PROFILE)
|
||||
ndp = ProvisionPower("ssh")
|
||||
ndp.power_user = "root"
|
||||
|
||||
for node in itertools.ifilter(
|
||||
lambda n: n.status == "discover", cluster.nodes
|
||||
):
|
||||
nd = ProvisionNode(node.id)
|
||||
nd.driver = pd
|
||||
nd.mac = node.mac
|
||||
nd.profile = pf
|
||||
nd.pxe = True
|
||||
nd.kopts = ""
|
||||
nd.power = ndp
|
||||
logging.debug(
|
||||
"Trying to save node %s into provision system: profile: %s ",
|
||||
node.id,
|
||||
pf.name
|
||||
)
|
||||
nd.save()
|
||||
logging.debug(
|
||||
"Trying to reboot node %s using %s "
|
||||
"in order to launch provisioning",
|
||||
node.id,
|
||||
ndp.power_type
|
||||
)
|
||||
nd.power_reboot()
|
||||
|
||||
message = {"method": "deploy", "args": {"var1": "Hello from nailgun"}}
|
||||
rpc.cast('mcollective', message)
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from settings import settings
|
||||
from wsgilog import WsgiLog
|
||||
|
||||
|
||||
class Log(WsgiLog):
|
||||
def __init__(self, application):
|
||||
WsgiLog.__init__(
|
||||
self,
|
||||
application,
|
||||
logformat='%(message)s',
|
||||
tofile=False,
|
||||
toprint=True,
|
||||
#file=settings.LOGFILE
|
||||
)
|
|
@ -9,11 +9,13 @@ import code
|
|||
|
||||
import web
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
|
||||
from api.handlers import check_client_content_type
|
||||
from api.models import engine
|
||||
from db import load_db_driver, syncdb
|
||||
from unit_test import TestRunner
|
||||
from urls import urls
|
||||
from logger import Log
|
||||
|
||||
logging.basicConfig(level="DEBUG")
|
||||
|
||||
|
@ -97,7 +99,7 @@ if __name__ == "__main__":
|
|||
logging.info("Running WSGI app...")
|
||||
server = web.httpserver.WSGIServer(
|
||||
("0.0.0.0", int(params.port)),
|
||||
app.wsgifunc()
|
||||
app.wsgifunc(Log)
|
||||
)
|
||||
try:
|
||||
rpc_thread.start()
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,49 @@
|
|||
import re
|
||||
|
||||
|
||||
class ProvisionException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ProvisionAlreadyExists(ProvisionException):
|
||||
pass
|
||||
|
||||
|
||||
class ProvisionDoesNotExist(ProvisionException):
|
||||
pass
|
||||
|
||||
|
||||
class ProvisionConfig:
|
||||
cn = 'nailgun.provision.driver.cobbler.Cobbler'
|
||||
|
||||
|
||||
class Provision:
|
||||
def __init__(self):
|
||||
raise NotImplementedError(
|
||||
"Try to use ProvisionFactory.getInstance() method."
|
||||
)
|
||||
|
||||
def save_profile(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def save_node(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ProvisionFactory:
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls, config=ProvisionConfig()):
|
||||
name = config.cn
|
||||
module_name = '.'.join(re.split(ur'\.', name)[:-1])
|
||||
class_name = re.split(ur'\.', name)[-1]
|
||||
return getattr(
|
||||
__import__(
|
||||
module_name,
|
||||
globals(),
|
||||
locals(),
|
||||
[class_name],
|
||||
-1
|
||||
),
|
||||
class_name
|
||||
)(config)
|
|
@ -0,0 +1,366 @@
|
|||
from provision import ProvisionException
|
||||
from provision import ProvisionAlreadyExists, ProvisionDoesNotExist
|
||||
from provision import Provision
|
||||
import logging
|
||||
import xmlrpclib
|
||||
|
||||
|
||||
class Cobbler(Provision):
|
||||
def __init__(self, config):
|
||||
self.logger = logging.getLogger('provision.cobbler')
|
||||
try:
|
||||
self.url = config.url
|
||||
self.user = config.user
|
||||
self.password = config.password
|
||||
except AttributeError as e:
|
||||
self.logger.error(
|
||||
'Provision configuration error.'
|
||||
' Not all necessary attributes are set properly.'
|
||||
)
|
||||
raise e
|
||||
|
||||
self.logger.debug(
|
||||
'Cobbler config: url="%s", user="%s", password="%s"',
|
||||
self.url,
|
||||
self.user,
|
||||
self.password
|
||||
)
|
||||
|
||||
try:
|
||||
self.server = xmlrpclib.Server(self.url)
|
||||
self.token = self.server.login(self.user, self.password)
|
||||
except ProvisionException as e:
|
||||
self.logger.error(
|
||||
'Error occured while connecting to provision server.'
|
||||
)
|
||||
raise e
|
||||
|
||||
def _get_any_profile(self):
|
||||
profiles = self.server.get_profiles(self.token)
|
||||
if profiles:
|
||||
return profiles[0]
|
||||
raise ProvisionException("There is no available profiles")
|
||||
|
||||
def system_by_name(self, name):
|
||||
systems = self.server.find_system({'name': name}, self.token)
|
||||
if systems:
|
||||
if len(systems) > 1:
|
||||
self.logger.error(
|
||||
"There are more than one system found by pattern: %s",
|
||||
name
|
||||
)
|
||||
raise ProvisionException(
|
||||
"There are more than one system found by pattern: %s",
|
||||
name
|
||||
)
|
||||
return systems[0]
|
||||
return None
|
||||
|
||||
# FIXME
|
||||
# IT NEEDED TO BE IMPLEMENTED AS ONLY METHOD FOR ADD AND EDIT
|
||||
def add_system(self, name, mac, power, profile, kopts=""):
|
||||
if self.system_by_name(name):
|
||||
self.logger.error(
|
||||
"Trying to add system that already exists: %s",
|
||||
name
|
||||
)
|
||||
raise ProvisionAlreadyExists(
|
||||
"System with name %s already exists. Try to edit it.",
|
||||
name
|
||||
)
|
||||
system_id = self.server.new_system(self.token)
|
||||
self.server.modify_system(
|
||||
system_id, 'name', name, self.token
|
||||
)
|
||||
self.server.modify_system(
|
||||
system_id, 'profile', profile.name, self.token
|
||||
)
|
||||
self.server.modify_system(
|
||||
system_id, 'kopts', kopts, self.token
|
||||
)
|
||||
self.server.modify_system(
|
||||
system_id, 'modify_interface', {
|
||||
"macaddress-eth0": mac,
|
||||
}, self.token
|
||||
)
|
||||
self.server.modify_system(
|
||||
system_id, 'power_type', power.power_type, self.token
|
||||
)
|
||||
if power.power_user:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_user', power.power_user, self.token
|
||||
)
|
||||
if power.power_pass:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_pass', power.power_pass, self.token
|
||||
)
|
||||
if power.power_id:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_id', power.power_id, self.token
|
||||
)
|
||||
if power.power_address:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_address', power.power_address, self.token
|
||||
)
|
||||
self.server.save_system(system_id, self.token)
|
||||
return self.system_by_name(name)
|
||||
|
||||
def edit_system(self, name, mac, power, profile, kopts=""):
|
||||
if not self.system_by_name(name):
|
||||
self.logger.error(
|
||||
"Trying to edit system that does not exist: %s",
|
||||
name
|
||||
)
|
||||
raise ProvisionDoesNotExist(
|
||||
"System with name %s does not exist. Try to edit it.",
|
||||
name
|
||||
)
|
||||
system_id = self.server.get_system_handle(name, self.token)
|
||||
self.server.modify_system(
|
||||
system_id, 'profile', profile.name, self.token
|
||||
)
|
||||
self.server.modify_system(
|
||||
system_id, 'kopts', kopts, self.token
|
||||
)
|
||||
self.server.modify_system(
|
||||
system_id, 'modify_interface',
|
||||
{
|
||||
"macaddress-eth0": mac,
|
||||
}, self.token
|
||||
)
|
||||
|
||||
self.server.modify_system(
|
||||
system_id, 'power_type', power.power_type, self.token
|
||||
)
|
||||
if power.power_user:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_user', power.power_user, self.token
|
||||
)
|
||||
if power.power_pass:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_pass', power.power_pass, self.token
|
||||
)
|
||||
if power.power_id:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_id', power.power_id, self.token
|
||||
)
|
||||
if power.power_address:
|
||||
self.server.modify_system(
|
||||
system_id, 'power_address', power.power_address, self.token
|
||||
)
|
||||
self.server.save_system(system_id, self.token)
|
||||
return self.system_by_name(name)
|
||||
|
||||
def power_system(self, name, power):
|
||||
if not self.system_by_name(name):
|
||||
self.logger.error(
|
||||
"Trying to power system that does not exist: %s" % name
|
||||
)
|
||||
raise ProvisionDoesNotExist(
|
||||
"System with name %s does not exist. Try to edit it." % name
|
||||
)
|
||||
if power not in ('on', 'off', 'reboot', 'status'):
|
||||
raise ValueError("Power has invalid value")
|
||||
system_id = self.server.get_system_handle(name, self.token)
|
||||
self.server.power_system(system_id, power, self.token)
|
||||
return self.system_by_name(name)
|
||||
|
||||
def handle_system(self, name, mac, power, profile, kopts=""):
|
||||
try:
|
||||
self.edit_system(name, mac, power, profile, kopts)
|
||||
self.logger.info("Edited system: %s" % name)
|
||||
except ProvisionDoesNotExist:
|
||||
self.add_system(name, mac, power, profile, kopts)
|
||||
self.logger.info("Added system: %s" % name)
|
||||
|
||||
def del_system(self, name):
|
||||
system = self.system_by_name(name)
|
||||
if not system:
|
||||
self.logger.error(
|
||||
"Trying to remove system that does not exist: %s" % name
|
||||
)
|
||||
raise ProvisionDoesNotExist(
|
||||
"There is no system with name %s" % name
|
||||
)
|
||||
self.server.remove_system(name, self.token)
|
||||
self.logger.info("Removed system %s" % name)
|
||||
|
||||
def profile_by_name(self, name):
|
||||
profiles = self.server.find_profile({'name': name}, self.token)
|
||||
if profiles:
|
||||
if len(profiles) > 1:
|
||||
self.logger.error(
|
||||
"There are more than one profile found by pattern: %s",
|
||||
name
|
||||
)
|
||||
raise ProvisionException(
|
||||
"There are more than one profile found by pattern: %s",
|
||||
name
|
||||
)
|
||||
return profiles[0]
|
||||
return None
|
||||
|
||||
# FIXME
|
||||
# IT NEEDED TO BE IMPLEMENTED AS ONLY METHOD FOR ADD AND EDIT
|
||||
def add_profile(self, name, distro, kickstart):
|
||||
if self.profile_by_name(name):
|
||||
self.logger.error(
|
||||
"Trying to add profile that already exists: %s" % name
|
||||
)
|
||||
raise ProvisionAlreadyExists(
|
||||
"Profile with name %s already exists. Try to edit it.",
|
||||
name
|
||||
)
|
||||
profile_id = self.server.new_profile(self.token)
|
||||
self.server.modify_profile(profile_id, 'name', name, self.token)
|
||||
self.server.modify_profile(profile_id, 'distro', distro, self.token)
|
||||
self.server.modify_profile(
|
||||
profile_id, 'kickstart', kickstart, self.token
|
||||
)
|
||||
self.server.save_profile(profile_id, self.token)
|
||||
return self.profile_by_name(name)
|
||||
|
||||
def edit_profile(self, name, distro, kickstart):
|
||||
if not self.profile_by_name(name):
|
||||
self.logger.error(
|
||||
"Trying to edit profile that does not exist: %s" % name
|
||||
)
|
||||
raise ProvisionDoesNotExist(
|
||||
"Profile with name %s does not exist. Try to add it." % name
|
||||
)
|
||||
profile_id = self.server.get_profile_handle(name, self.token)
|
||||
self.server.modify_profile(profile_id, 'distro', distro, self.token)
|
||||
self.server.modify_profile(
|
||||
profile_id, 'kickstart', kickstart, self.token
|
||||
)
|
||||
self.server.save_profile(profile_id, self.token)
|
||||
return self.profile_by_name(name)
|
||||
|
||||
def handle_profile(self, name, distro, seed):
|
||||
try:
|
||||
self.edit_profile(name, distro, seed)
|
||||
self.logger.info("Edited profile: %s" % name)
|
||||
except ProvisionDoesNotExist:
|
||||
self.add_profile(name, distro, seed)
|
||||
self.logger.info("Added profile: %s" % name)
|
||||
|
||||
def del_profile(self, name):
|
||||
profile = self.profile_by_name(name)
|
||||
if not profile:
|
||||
self.logger.error(
|
||||
"Trying to remove profile that does not exist: %s" % name
|
||||
)
|
||||
raise ProvisionDoesNotExist(
|
||||
"There is no profile with name %s" % name
|
||||
)
|
||||
self.server.remove_profile(name, self.token)
|
||||
self.logger.info("Removed profile: %s" % name)
|
||||
|
||||
def distro_by_name(self, name):
|
||||
distros = self.server.find_distro({'name': name}, self.token)
|
||||
if distros:
|
||||
if len(distros) > 1:
|
||||
self.logger.error(
|
||||
"There are more than one distro found by pattern: %s",
|
||||
name
|
||||
)
|
||||
raise ProvisionException(
|
||||
"There are more than one distro found by pattern %s",
|
||||
name
|
||||
)
|
||||
return distros[0]
|
||||
return None
|
||||
|
||||
# FIXME
|
||||
# IT NEEDED TO BE IMPLEMENTED AS ONLY METHOD FOR ADD AND EDIT
|
||||
def add_distro(self, name, kernel, initrd, arch, breed, osversion):
|
||||
if self.distro_by_name(name):
|
||||
self.logger.error(
|
||||
"Trying to add distro that already exists: %s" % name
|
||||
)
|
||||
raise ProvisionAlreadyExists(
|
||||
"Distro with name %s already exists. Try to edit it." % name
|
||||
)
|
||||
distro_id = self.server.new_distro(self.token)
|
||||
self.server.modify_distro(distro_id, 'name', name, self.token)
|
||||
self.server.modify_distro(distro_id, 'kernel', kernel, self.token)
|
||||
self.server.modify_distro(distro_id, 'initrd', initrd, self.token)
|
||||
self.server.modify_distro(distro_id, 'arch', arch, self.token)
|
||||
self.server.modify_distro(distro_id, 'breed', breed, self.token)
|
||||
self.server.modify_distro(
|
||||
distro_id, 'os_version', osversion, self.token
|
||||
)
|
||||
self.server.save_distro(distro_id, self.token)
|
||||
return self.distro_by_name(name)
|
||||
|
||||
def edit_distro(self, name, kernel, initrd, arch, breed, osversion):
|
||||
if not self.distro_by_name(name):
|
||||
self.logger.error(
|
||||
"Trying to edit distro that does not exist: %s" % name
|
||||
)
|
||||
raise ProvisionDoesNotExist(
|
||||
"Distro with name %s does not exist. Try to add it." % name
|
||||
)
|
||||
distro_id = self.server.get_distro_handle(name, self.token)
|
||||
self.server.modify_distro(distro_id, 'kernel', kernel, self.token)
|
||||
self.server.modify_distro(distro_id, 'initrd', initrd, self.token)
|
||||
self.server.modify_distro(distro_id, 'arch', arch, self.token)
|
||||
self.server.modify_distro(distro_id, 'breed', breed, self.token)
|
||||
self.server.modify_distro(
|
||||
distro_id, 'os_version', osversion, self.token
|
||||
)
|
||||
self.server.save_distro(distro_id, self.token)
|
||||
return self.distro_by_name(name)
|
||||
|
||||
def handle_distro(self, name, kernel, initrd, arch, os, osversion):
|
||||
try:
|
||||
self.edit_distro(name, kernel, initrd, arch, os, osversion)
|
||||
self.logger.info("Edited distro: %s" % name)
|
||||
except ProvisionDoesNotExist:
|
||||
self.add_distro(name, kernel, initrd, arch, os, osversion)
|
||||
self.logger.info("Added distro: %s" % name)
|
||||
|
||||
def del_distro(self, name):
|
||||
distro = self.distro_by_name(name)
|
||||
if not distro:
|
||||
self.logger.error(
|
||||
"Trying to remove distro that does not exist: %s" % name
|
||||
)
|
||||
raise ProvisionDoesNotExist(
|
||||
"There is no distro with name %s" % name
|
||||
)
|
||||
self.server.remove_distro(name, self.token)
|
||||
self.logger.info("Removed distro %s" % name)
|
||||
|
||||
# API
|
||||
|
||||
def save_profile(self, profile):
|
||||
self.handle_distro(profile.name,
|
||||
profile.kernel,
|
||||
profile.initrd,
|
||||
profile.arch,
|
||||
profile.os,
|
||||
profile.osversion)
|
||||
self.handle_profile(profile.name,
|
||||
profile.name,
|
||||
profile.seed)
|
||||
|
||||
def save_node(self, node):
|
||||
self.handle_system(node.name,
|
||||
node.mac,
|
||||
node.power,
|
||||
node.profile,
|
||||
node.kopts,
|
||||
)
|
||||
|
||||
def power_on(self, node):
|
||||
self.power_system(node.name, 'on')
|
||||
|
||||
def power_off(self, node):
|
||||
self.power_system(node.name, 'off')
|
||||
|
||||
def power_reboot(self, node):
|
||||
self.power_system(node.name, 'reboot')
|
||||
|
||||
def power_status(self, node):
|
||||
raise NotImplementedError
|
|
@ -0,0 +1,81 @@
|
|||
import re
|
||||
from provision import ProvisionException
|
||||
import logging
|
||||
|
||||
|
||||
class ModelObject(object):
|
||||
_driver = None
|
||||
|
||||
@property
|
||||
def driver(self):
|
||||
if self._driver is None:
|
||||
raise ProvisionException("Driver is not set properly.")
|
||||
return self._driver
|
||||
|
||||
@driver.setter
|
||||
def driver(self, driver):
|
||||
self._driver = driver
|
||||
|
||||
|
||||
class Validator:
|
||||
_supported_os = (
|
||||
"ubuntu",
|
||||
"redhat",
|
||||
)
|
||||
|
||||
_supported_osversion = (
|
||||
"precise",
|
||||
"rhel6",
|
||||
)
|
||||
|
||||
_supported_arch = (
|
||||
"x86_64",
|
||||
)
|
||||
|
||||
_supported_platform = (
|
||||
("ubuntu", "precise", "x86_64"),
|
||||
("redhat", "rhel6", "x86_64"),
|
||||
)
|
||||
|
||||
_supported_powertypes = (
|
||||
"virsh",
|
||||
"ssh",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_mac_valid(cls, mac):
|
||||
rex = re.compile(ur'^([0-9abcdef]{2}:){5}[0-9abcdef]{2}$', re.I)
|
||||
return rex.match(mac)
|
||||
|
||||
@classmethod
|
||||
def is_os_valid(cls, os):
|
||||
return os in cls._supported_os
|
||||
|
||||
@classmethod
|
||||
def is_osversion_valid(cls, osversion):
|
||||
return osversion in cls._supported_osversion
|
||||
|
||||
@classmethod
|
||||
def is_arch_valid(cls, arch):
|
||||
return arch in cls._supported_arch
|
||||
|
||||
@classmethod
|
||||
def is_platform_valid(cls, os, osversion, arch):
|
||||
return (os, osversion, arch) in cls._supported_platform
|
||||
|
||||
# FIXME
|
||||
# IT IS NEEDED TO BE CHECKED IF PROVISION ALREADY HAS THAT PROFILE
|
||||
# IF NOT THEN PROFILE IS OBVIOUSLY INVALID
|
||||
@classmethod
|
||||
def is_profile_valid(cls, profile):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def is_powertype_valid(cls, powertype):
|
||||
return powertype in cls._supported_powertypes
|
||||
|
||||
# FIXME
|
||||
# IT IS NEEDED TO BE CHECKED IF POWER IS VALID
|
||||
@classmethod
|
||||
def is_power_valid(cls, power):
|
||||
return True
|
|
@ -0,0 +1,89 @@
|
|||
import logging
|
||||
from provision import ProvisionException
|
||||
from . import ModelObject, Validator
|
||||
|
||||
|
||||
class Node(ModelObject):
|
||||
_mac = None
|
||||
_profile = None
|
||||
_kopts = ""
|
||||
_pxe = False
|
||||
_power = None
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.logger = logging.getLogger('provision.model.node')
|
||||
|
||||
def save(self):
|
||||
self.driver.save_node(self)
|
||||
|
||||
@property
|
||||
def mac(self):
|
||||
if not self._mac:
|
||||
raise ProvisionException("Mac is not set properly")
|
||||
return self._mac
|
||||
|
||||
@mac.setter
|
||||
def mac(self, mac):
|
||||
if not Validator.is_mac_valid(mac):
|
||||
raise ProvisionException("Mac is not valid")
|
||||
self._mac = mac
|
||||
|
||||
@property
|
||||
def profile(self):
|
||||
if not self._profile:
|
||||
raise ProvisionException("Profile is not set properly")
|
||||
return self._profile
|
||||
|
||||
@profile.setter
|
||||
def profile(self, profile):
|
||||
if not Validator.is_profile_valid(profile):
|
||||
raise ProvisionException("Profile is not valid")
|
||||
self._profile = profile
|
||||
|
||||
@property
|
||||
def kopts(self):
|
||||
self.logger.debug("Node kopts getter: %s" % self._kopts)
|
||||
return self._kopts
|
||||
|
||||
@kopts.setter
|
||||
def kopts(self, kopts):
|
||||
self.logger.debug("Node kopts setter: %s" % kopts)
|
||||
self._kopts = kopts
|
||||
|
||||
@property
|
||||
def pxe(self):
|
||||
self.logger.debug("Node pxe getter: %s" % str(self._pxe))
|
||||
return self._pxe
|
||||
|
||||
@pxe.setter
|
||||
def pxe(self, pxe):
|
||||
self.logger.debug("Node pxe setter: %s" % str(pxe))
|
||||
if pxe:
|
||||
self._pxe = True
|
||||
else:
|
||||
self._pxe = False
|
||||
|
||||
@property
|
||||
def power(self):
|
||||
if not self._power:
|
||||
raise ProvisionException("Power is not set properly")
|
||||
return self._power
|
||||
|
||||
@power.setter
|
||||
def power(self, power):
|
||||
if not Validator.is_power_valid(power):
|
||||
raise ProvisionException("Power is not valid")
|
||||
self._power = power
|
||||
|
||||
def power_on(self):
|
||||
self.driver.power_on(self)
|
||||
|
||||
def power_off(self):
|
||||
self.driver.power_off(self)
|
||||
|
||||
def power_reboot(self):
|
||||
self.driver.power_reboot(self)
|
||||
|
||||
def power_status(self):
|
||||
self.driver.power_status(self)
|
|
@ -0,0 +1,52 @@
|
|||
import logging
|
||||
from provision import ProvisionException
|
||||
from . import Validator
|
||||
|
||||
|
||||
class Power:
|
||||
_power_user = None
|
||||
_power_pass = None
|
||||
_power_address = None
|
||||
_power_id = None
|
||||
|
||||
def __init__(self, power_type):
|
||||
if Validator.is_powertype_valid(power_type):
|
||||
self._power_type = power_type
|
||||
else:
|
||||
raise ProvisionException("Power type is not valid")
|
||||
|
||||
@property
|
||||
def power_type(self):
|
||||
return self._power_type
|
||||
|
||||
@property
|
||||
def power_user(self):
|
||||
return self._power_user
|
||||
|
||||
@power_user.setter
|
||||
def power_user(self, power_user):
|
||||
self._power_user = power_user
|
||||
|
||||
@property
|
||||
def power_pass(self):
|
||||
return self._power_pass
|
||||
|
||||
@power_pass.setter
|
||||
def power_pass(self, power_pass):
|
||||
self._power_pass = power_pass
|
||||
|
||||
@property
|
||||
def power_address(self):
|
||||
return self._power_address
|
||||
|
||||
@power_address.setter
|
||||
def power_address(self, power_address):
|
||||
self._power_address = power_address
|
||||
|
||||
@property
|
||||
def power_id(self):
|
||||
return self._power_id
|
||||
|
||||
@power_id.setter
|
||||
def power_id(self, power_id):
|
||||
self._power_id = power_id
|
|
@ -0,0 +1,102 @@
|
|||
import logging
|
||||
from . import ModelObject, Validator
|
||||
from provision import ProvisionException
|
||||
|
||||
|
||||
class Profile(ModelObject):
|
||||
_arch = None
|
||||
_kernel = None
|
||||
_initrd = None
|
||||
_os = None
|
||||
_osversion = None
|
||||
_seed = None
|
||||
_kopts = ""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.logger = logging.getLogger('provision.model.profile')
|
||||
|
||||
def save(self):
|
||||
if not Validator.is_platform_valid(
|
||||
self._os, self._osversion, self._arch
|
||||
):
|
||||
raise ProvisionException("Platform is not valid")
|
||||
self.driver.save_profile(self)
|
||||
|
||||
@property
|
||||
def arch(self):
|
||||
if not self._arch:
|
||||
raise ProvisionException("Arch is not set properly")
|
||||
return self._arch
|
||||
|
||||
@arch.setter
|
||||
def arch(self, arch):
|
||||
if not Validator.is_arch_valid(arch):
|
||||
raise ProvisionException("Arch is not valid")
|
||||
self._arch = arch
|
||||
|
||||
@property
|
||||
def kernel(self):
|
||||
if not self._kernel:
|
||||
raise ProvisionException("Kernel is not set properly")
|
||||
return self._kernel
|
||||
|
||||
@kernel.setter
|
||||
def kernel(self, kernel):
|
||||
self._kernel = kernel
|
||||
|
||||
@property
|
||||
def initrd(self):
|
||||
if not self._initrd:
|
||||
raise ProvisionException("Initrd is not set properly")
|
||||
return self._initrd
|
||||
|
||||
@initrd.setter
|
||||
def initrd(self, initrd):
|
||||
self._initrd = initrd
|
||||
|
||||
@property
|
||||
def os(self):
|
||||
if not self._os:
|
||||
raise ProvisionException("Os is not set properly")
|
||||
return self._os
|
||||
|
||||
@os.setter
|
||||
def os(self, os):
|
||||
if not Validator.is_os_valid(os):
|
||||
raise ProvisionException("Os is not valid")
|
||||
self._os = os
|
||||
|
||||
@property
|
||||
def osversion(self):
|
||||
if not self._osversion:
|
||||
raise ProvisionException("Osversion is not set properly")
|
||||
return self._osversion
|
||||
|
||||
@osversion.setter
|
||||
def osversion(self, osversion):
|
||||
if not Validator.is_osversion_valid(osversion):
|
||||
raise ProvisionException("Osversion is not valid")
|
||||
self._osversion = osversion
|
||||
|
||||
@property
|
||||
def seed(self):
|
||||
if not self._seed:
|
||||
raise ProvisionException("Seed is not set properly")
|
||||
self.logger.debug("Profile seed getter: %s" % self._seed)
|
||||
return self._seed
|
||||
|
||||
@seed.setter
|
||||
def seed(self, seed):
|
||||
self.logger.debug("Profile seed setter: %s" % seed)
|
||||
self._seed = seed
|
||||
|
||||
@property
|
||||
def kopts(self):
|
||||
self.logger.debug("Profile kopts getter: %s" % self._kopts)
|
||||
return self._kopts
|
||||
|
||||
@kopts.setter
|
||||
def kopts(self, kopts):
|
||||
self.logger.debug("Profile kopts setter: %s" % kopts)
|
||||
self._kopts = kopts
|
|
@ -0,0 +1,128 @@
|
|||
from model import Validator
|
||||
from model.profile import Profile
|
||||
from model.node import Node
|
||||
from model.power import Power
|
||||
from nose.tools import eq_
|
||||
|
||||
|
||||
class TestValidator:
|
||||
def setUp(self):
|
||||
self.mac = "c8:0a:a9:a6:ff:28"
|
||||
self.platform = ("ubuntu", "precise", "x86_64")
|
||||
self.os = "ubuntu"
|
||||
self.osversion = "precise"
|
||||
self.arch = "x86_64"
|
||||
|
||||
def test_is_mac_valid(self):
|
||||
assert Validator.is_mac_valid(self.mac)
|
||||
|
||||
def test_is_platform_valid(self):
|
||||
assert Validator.is_platform_valid(
|
||||
self.platform[0],
|
||||
self.platform[1],
|
||||
self.platform[2]
|
||||
)
|
||||
|
||||
def test_is_os_valid(self):
|
||||
assert Validator.is_os_valid(self.os)
|
||||
|
||||
def test_is_osversion_valid(self):
|
||||
assert Validator.is_osversion_valid(self.osversion)
|
||||
|
||||
def test_is_arch_valid(self):
|
||||
assert Validator.is_arch_valid(self.arch)
|
||||
|
||||
|
||||
class TestProfile:
|
||||
def setUp(self):
|
||||
self.profile = Profile('profile')
|
||||
self.arch = "x86_64"
|
||||
self.os = "ubuntu"
|
||||
self.osversion = "precise"
|
||||
self.kernel = "kernel"
|
||||
self.initrd = "initrd"
|
||||
self.seed = "seed"
|
||||
self.kopts = "kopts"
|
||||
|
||||
def test_arch(self):
|
||||
self.profile.arch = self.arch
|
||||
eq_(self.profile.arch, self.arch)
|
||||
|
||||
def test_os(self):
|
||||
self.profile.os = self.os
|
||||
eq_(self.profile.os, self.os)
|
||||
|
||||
def test_osversion(self):
|
||||
self.profile.osversion = self.osversion
|
||||
eq_(self.profile.osversion, self.osversion)
|
||||
|
||||
def test_kernel(self):
|
||||
self.profile.kernel = self.kernel
|
||||
eq_(self.profile.kernel, self.kernel)
|
||||
|
||||
def test_initrd(self):
|
||||
self.profile.initrd = self.initrd
|
||||
eq_(self.profile.initrd, self.initrd)
|
||||
|
||||
def test_seed(self):
|
||||
self.profile.seed = self.seed
|
||||
eq_(self.profile.seed, self.seed)
|
||||
|
||||
def test_kopts(self):
|
||||
self.profile.kopts = self.kopts
|
||||
eq_(self.profile.kopts, self.kopts)
|
||||
|
||||
|
||||
class TestNode:
|
||||
def setUp(self):
|
||||
self.node = Node('node')
|
||||
self.mac = "c8:0a:a9:a6:ff:28"
|
||||
self.profile = Profile('profile')
|
||||
self.kopts = "kopts"
|
||||
self.pxe = True
|
||||
self.power = Power('ssh')
|
||||
|
||||
def test_mac(self):
|
||||
self.node.mac = self.mac
|
||||
eq_(self.node.mac, self.mac)
|
||||
|
||||
def test_profile(self):
|
||||
self.node.profile = self.profile
|
||||
eq_(self.node.profile, self.profile)
|
||||
|
||||
def test_kopts(self):
|
||||
self.node.kopts = self.kopts
|
||||
eq_(self.node.kopts, self.kopts)
|
||||
|
||||
def test_pxe(self):
|
||||
self.node.pxe = self.pxe
|
||||
eq_(self.node.pxe, self.pxe)
|
||||
|
||||
def test_power(self):
|
||||
self.node.power = self.power
|
||||
eq_(self.node.power, self.power)
|
||||
|
||||
|
||||
class TestPower:
|
||||
def setUp(self):
|
||||
self.power = Power('ssh')
|
||||
self.power_user = "user"
|
||||
self.power_pass = "pass"
|
||||
self.power_address = "localhost"
|
||||
self.power_id = "localhost"
|
||||
|
||||
def test_power_user(self):
|
||||
self.power.power_user = self.power_user
|
||||
eq_(self.power.power_user, self.power_user)
|
||||
|
||||
def test_power_pass(self):
|
||||
self.power.power_pass = self.power_pass
|
||||
eq_(self.power.power_pass, self.power_pass)
|
||||
|
||||
def test_power_address(self):
|
||||
self.power.power_address = self.power_address
|
||||
eq_(self.power.power_address, self.power_address)
|
||||
|
||||
def test_power_id(self):
|
||||
self.power.power_id = self.power_id
|
||||
eq_(self.power.power_id, self.power_id)
|
|
@ -0,0 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
LOGFILE = "nailgun.log"
|
||||
|
||||
COBBLER_URL = "http://localhost/cobbler_api"
|
||||
COBBLER_USER = "cobbler"
|
||||
COBBLER_PASSWORD = "cobbler"
|
||||
COBBLER_PROFILE = "centos-6.3-x86_64"
|
|
@ -24,3 +24,5 @@ NETWORK_POOLS = {
|
|||
NET_EXCLUDE = ['10.20.0.0/24']
|
||||
|
||||
VLANS = range(100, 1000)
|
||||
|
||||
from .extrasettings import *
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
import json
|
||||
from paste.fixture import TestApp
|
||||
from random import randint
|
||||
from unittest.case import TestCase
|
||||
import re
|
||||
|
||||
import mock
|
||||
from paste.fixture import TestApp
|
||||
from sqlalchemy.orm.events import orm
|
||||
|
||||
from api.models import engine, Node, Release, Cluster, Role
|
||||
from api.urls import urls
|
||||
from db import dropdb, syncdb, flush
|
||||
from manage import app
|
||||
from db import dropdb, syncdb, flush
|
||||
|
||||
|
||||
class BaseHandlers(TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseHandlers, self).__init__(*args, **kwargs)
|
||||
self.mock = mock
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
dropdb()
|
||||
|
@ -31,6 +40,10 @@ class BaseHandlers(TestCase):
|
|||
'memory': 'a'}
|
||||
return metadata
|
||||
|
||||
def _generate_random_mac(self):
|
||||
mac = [randint(0x00, 0x7f) for _ in xrange(6)]
|
||||
return ':'.join(map(lambda x: "%02x" % x, mac))
|
||||
|
||||
def create_release_api(self):
|
||||
resp = self.app.post(
|
||||
'/api/releases',
|
||||
|
@ -45,7 +58,7 @@ class BaseHandlers(TestCase):
|
|||
|
||||
def create_default_node(self):
|
||||
node = Node()
|
||||
node.mac = u"ASDFGHJKLMNOPR"
|
||||
node.mac = self._generate_random_mac()
|
||||
self.db.add(node)
|
||||
self.db.commit()
|
||||
return node
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import json
|
||||
|
||||
from base import BaseHandlers
|
||||
from base import reverse
|
||||
|
||||
|
||||
class TestClusterChangesHandler(BaseHandlers):
|
||||
def test_cluster_starts_deploy(self):
|
||||
cluster = self.create_default_cluster()
|
||||
resp = self.app.put(
|
||||
reverse(
|
||||
'ClusterChangesHandler',
|
||||
kwargs={'cluster_id': cluster.id}),
|
||||
headers=self.default_headers
|
||||
)
|
||||
self.assertEquals(200, resp.status)
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import unittest
|
||||
import json
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
from settings import settings
|
||||
from base import BaseHandlers
|
||||
from base import reverse
|
||||
from provision import ProvisionConfig
|
||||
from provision import ProvisionFactory
|
||||
from provision.model.profile import Profile as ProvisionProfile
|
||||
from provision.model.node import Node as ProvisionNode
|
||||
from provision.model.power import Power as ProvisionPower
|
||||
|
||||
|
||||
class TestProvisioning(BaseHandlers):
|
||||
def test_nodes_in_cluster(self):
|
||||
cluster = self.create_default_cluster()
|
||||
node = self.create_default_node()
|
||||
node2 = self.create_default_node()
|
||||
node2.status = "discover"
|
||||
node3 = self.create_default_node()
|
||||
cluster.nodes.append(node)
|
||||
cluster.nodes.append(node2)
|
||||
self.db.add(cluster)
|
||||
self.db.commit()
|
||||
|
||||
self.assertEqual(len(cluster.nodes), 2)
|
||||
ProvisionFactory.getInstance = self.mock.MagicMock()
|
||||
|
||||
resp = self.app.put(
|
||||
reverse(
|
||||
'ClusterChangesHandler',
|
||||
kwargs={"cluster_id": cluster.id}
|
||||
),
|
||||
"",
|
||||
headers=self.default_headers,
|
||||
expect_errors=True
|
||||
)
|
||||
self.assertEquals(200, resp.status)
|
|
@ -4,8 +4,8 @@
|
|||
"description": "Description for Release",
|
||||
"networks_metadata": [
|
||||
{"name": "floating", "access": "public"},
|
||||
{"name": "admin", "access": "private"},
|
||||
{"name": "storage", "access": "private"}
|
||||
{"name": "admin", "access": "private10"},
|
||||
{"name": "storage", "access": "private172"}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue