Commit b41b981f authored by Marco Mariani's avatar Marco Mariani

cli refactoring: node register

parent 61ee9971
...@@ -76,6 +76,7 @@ setup(name=name, ...@@ -76,6 +76,7 @@ setup(name=name,
'cache lookup = slapos.cli.cache:CacheLookupCommand', 'cache lookup = slapos.cli.cache:CacheLookupCommand',
'node bang = slapos.cli.bang:BangCommand', 'node bang = slapos.cli.bang:BangCommand',
'node format = slapos.cli.format:FormatCommand', 'node format = slapos.cli.format:FormatCommand',
'node register = slapos.cli.register:RegisterCommand',
] ]
}, },
test_suite="slapos.tests", test_suite="slapos.tests",
......
# -*- coding: utf-8 -*-
import logging
import sys
from cliff.command import Command
from slapos.register.register import do_register, RegisterConfig
class RegisterCommand(Command):
log = logging.getLogger('Register')
def get_parser(self, prog_name):
ap = super(RegisterCommand, self).get_parser(prog_name)
ap.add_argument('node_name',
help='Name of the node')
ap.add_argument('--interface-name',
help='Interface name to access internet',
default='eth0')
ap.add_argument('--master-url',
help='URL of SlapOS master',
default='https://slap.vifib.com')
ap.add_argument('--master-url-web',
help='URL of SlapOS Master webservice to register certificates',
default='https://www.slapos.org')
ap.add_argument('--partition-number',
help='Number of partition on computer',
default='10',
type=int)
ap.add_argument('--ipv4-local-network',
help='Base of ipv4 local network',
default='10.0.0.0/16')
ap.add_argument('--ipv6-interface',
help='Interface name to get ipv6',
default='')
ap.add_argument('--login',
help='User login on SlapOS Master webservice')
ap.add_argument('--password',
help='User password on SlapOs Master webservice')
ap.add_argument('-t', '--create-tap',
help='Will trigger creation of one virtual "tap" interface per '
'Partition and attach it to primary interface. Requires '
'primary interface to be a bridge. defaults to false. '
'Needed to host virtual machines.',
default=False,
action='store_true')
ap.add_argument('-n', '--dry-run',
help='Simulate the execution steps',
default=False,
action='store_true')
return ap
def take_action(self, args):
#if args.password and not args.login:
# # XXX we don't have the parser now, maybe could be handled with an argument group?
# ap.error('Please enter your login with your password')
try:
config = RegisterConfig(logger=self.log)
config.setConfig(args)
return_code = do_register(config)
except SystemExit, err:
# Catch exception raised by optparse
return_code = err
sys.exit(return_code)
...@@ -27,9 +27,11 @@ ...@@ -27,9 +27,11 @@
# #
############################################################################## ##############################################################################
# XXX dry_run will happily register a new node on the slapos master. Isn't it supposed to be no-op?
# XXX does not create 'log' directory (required by slap2 entry point)
import argparse import argparse
import base64
import ConfigParser import ConfigParser
import getpass import getpass
import logging import logging
...@@ -41,52 +43,25 @@ import tempfile ...@@ -41,52 +43,25 @@ import tempfile
import urllib2 import urllib2
class SlapError(Exception): def authenticate(request, login, password):
""" auth = '%s:%s' % (login, password)
Slap error authheader = 'Basic %s' % auth.encode('base64').rstrip()
""" request.add_header('Authorization', authheader)
def __init__(self, message):
self.msg = message
class UsageError(SlapError):
pass
class ExecError(SlapError):
pass
def get_login():
"""Get user id and encode it for basic identification"""
login = raw_input("SlapOS Master Login: ")
password = getpass.getpass()
identification = base64.encodestring('%s:%s' % (login, password))[:-1]
return identification
def check_credentials(url, login, password):
def check_login(identification, master_url_web):
"""Check if logged correctly on SlapOS Master""" """Check if logged correctly on SlapOS Master"""
request = urllib2.Request(master_url_web) request = urllib2.Request(url)
# Prepare header for basic authentification authenticate(request, login, password)
authheader = "Basic %s" % identification return 'Logout' in urllib2.urlopen(request).read()
request.add_header("Authorization", authheader)
home_page_url = urllib2.urlopen(request).read()
if 'Logout' in home_page_url:
return 1
else:
return 0
def get_certificates(identification, node_name, master_url_web): def get_certificates(master_url_web, node_name, login, password):
"""Download certificates from SlapOS Master""" """Download certificates from SlapOS Master"""
register_server_url = '/'.join([master_url_web, ("add-a-server/WebSection_registerNewComputer?dialog_id=WebSection_viewServerInformationDialog&dialog_method=WebSection_registerNewComputer&title={}&object_path=/erp5/web_site_module/hosting/add-a-server&update_method=&cancel_url=https%3A//www.vifib.net/add-a-server/WebSection_viewServerInformationDialog&Base_callDialogMethod=&field_your_title=Essai1&dialog_category=None&form_id=view".format(node_name))]) register_server_url = '/'.join([master_url_web, ("add-a-server/WebSection_registerNewComputer?dialog_id=WebSection_viewServerInformationDialog&dialog_method=WebSection_registerNewComputer&title={}&object_path=/erp5/web_site_module/hosting/add-a-server&update_method=&cancel_url=https%3A//www.vifib.net/add-a-server/WebSection_viewServerInformationDialog&Base_callDialogMethod=&field_your_title=Essai1&dialog_category=None&form_id=view".format(node_name))])
request = urllib2.Request(register_server_url) request = urllib2.Request(register_server_url)
# Prepare header for basic authentification authenticate(request, login, password)
authheader = "Basic %s" % identification return urllib2.urlopen(request).read()
request.add_header("Authorization", authheader)
url = urllib2.urlopen(request)
page = url.read()
return page
def parse_certificates(source): def parse_certificates(source):
...@@ -95,7 +70,7 @@ def parse_certificates(source): ...@@ -95,7 +70,7 @@ def parse_certificates(source):
c_end = source.find("</textarea>", c_start) c_end = source.find("</textarea>", c_start)
k_start = source.find("-----BEGIN PRIVATE KEY-----") k_start = source.find("-----BEGIN PRIVATE KEY-----")
k_end = source.find("</textarea>", k_start) k_end = source.find("</textarea>", k_start)
return [source[c_start:c_end], source[k_start:k_end]] return source[c_start:c_end], source[k_start:k_end]
def get_computer_name(certificate): def get_computer_name(certificate):
...@@ -104,39 +79,37 @@ def get_computer_name(certificate): ...@@ -104,39 +79,37 @@ def get_computer_name(certificate):
i = certificate.find("/email", k) i = certificate.find("/email", k)
return certificate[k:i] return certificate[k:i]
def save_former_config(config): def save_former_config(config):
"""Save former configuration if found""" """Save former configuration if found"""
# Check for config file in /etc/opt/slapos/ # Check for config file in /etc/opt/slapos/
if os.path.exists('/etc/opt/slapos/slapos.cfg'): if os.path.exists('/etc/opt/slapos/slapos.cfg'):
former_slapos_configuration = '/etc/opt/slapos' former = '/etc/opt/slapos'
else: else:
former_slapos_configuration = 0 return
if former_slapos_configuration:
saved_slapos_configuration = former_slapos_configuration + '.old' saved = former + '.old'
while True: while True:
if os.path.exists(saved_slapos_configuration): if os.path.exists(saved):
print "Slapos configuration detected in %s" % saved_slapos_configuration print "Slapos configuration detected in %s" % saved
if saved_slapos_configuration[len(saved_slapos_configuration) - 1] != 'd' : if saved[-1] != 'd':
saved_slapos_configuration = saved_slapos_configuration[:len(saved_slapos_configuration) - 1] \ saved = saved[:-1] + str(int(saved[-1]) + 1)
+ str(int(saved_slapos_configuration[len(saved_slapos_configuration) - 1]) + 1 ) else:
else: saved += '.1'
saved_slapos_configuration += ".1" else: break
else: break config.logger.info("Former slapos configuration detected in %s moving to %s" % (former, saved))
config.logger.info("Former slapos configuration detected in %s moving to %s" % (former_slapos_configuration, saved_slapos_configuration)) shutil.move(former, saved)
shutil.move(former_slapos_configuration, saved_slapos_configuration)
def get_slapos_conf_example(): def get_slapos_conf_example():
""" """
Get slapos.cfg.example and return its path Get slapos.cfg.example and return its path
""" """
register_server_url = "http://git.erp5.org/gitweb/slapos.core.git/blob_plain/HEAD:/slapos.cfg.example" request = urllib2.Request('http://git.erp5.org/gitweb/slapos.core.git/blob_plain/HEAD:/slapos.cfg.example')
request = urllib2.Request(register_server_url) req = urllib2.urlopen(request)
url = urllib2.urlopen(request)
page = url.read()
_, path = tempfile.mkstemp() _, path = tempfile.mkstemp()
slapos_cfg_example = open(path,'w') with open(path, 'w') as fout:
slapos_cfg_example.write(page) fout.write(req.read())
slapos_cfg_example.close()
return path return path
...@@ -144,11 +117,11 @@ def slapconfig(config): ...@@ -144,11 +117,11 @@ def slapconfig(config):
"""Base Function to configure slapos in /etc/opt/slapos""" """Base Function to configure slapos in /etc/opt/slapos"""
dry_run = config.dry_run dry_run = config.dry_run
# Create slapos configuration directory if needed # Create slapos configuration directory if needed
slap_configuration_directory = os.path.normpath(config.slapos_configuration) slap_conf_dir = os.path.normpath(config.slapos_configuration)
# Make sure everybody can read slapos configuration directory: # Make sure everybody can read slapos configuration directory:
# Add +x to directories in path # Add +x to directories in path
directory = os.path.dirname(slap_configuration_directory) directory = os.path.dirname(slap_conf_dir)
while True: while True:
if os.path.dirname(directory) == directory: if os.path.dirname(directory) == directory:
break break
...@@ -156,100 +129,84 @@ def slapconfig(config): ...@@ -156,100 +129,84 @@ def slapconfig(config):
os.chmod(directory, os.stat(directory).st_mode | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH) os.chmod(directory, os.stat(directory).st_mode | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
directory = os.path.dirname(directory) directory = os.path.dirname(directory)
if not os.path.exists(slap_configuration_directory): if not os.path.exists(slap_conf_dir):
config.logger.info ("Creating directory: %s" % slap_configuration_directory) config.logger.info("Creating directory: %s" % slap_conf_dir)
if not dry_run: if not dry_run:
os.mkdir(slap_configuration_directory, 0711) os.mkdir(slap_conf_dir, 0o711)
user_certificate_repository_path = os.path.join(slap_configuration_directory,'ssl') user_certificate_repository_path = os.path.join(slap_conf_dir,'ssl')
if not os.path.exists(user_certificate_repository_path): if not os.path.exists(user_certificate_repository_path):
config.logger.info ("Creating directory: %s" % user_certificate_repository_path) config.logger.info("Creating directory: %s" % user_certificate_repository_path)
if not dry_run: if not dry_run:
os.mkdir(user_certificate_repository_path, 0711) os.mkdir(user_certificate_repository_path, 0o711)
key_file = os.path.join(user_certificate_repository_path, 'key') key_file = os.path.join(user_certificate_repository_path, 'key')
cert_file = os.path.join(user_certificate_repository_path, 'certificate') cert_file = os.path.join(user_certificate_repository_path, 'certificate')
for (src, dst) in [(config.key, key_file), (config.certificate, for src, dst in [
cert_file)]: (config.key, key_file),
config.logger.info ("Copying to %r, and setting minimum privileges" % dst) (config.certificate, cert_file)
]:
config.logger.info("Copying to %r, and setting minimum privileges" % dst)
if not dry_run: if not dry_run:
destination = open(dst,'w') with open(dst, 'w') as destination:
destination.write(''.join(src)) destination.write(''.join(src))
destination.close() os.chmod(dst, 0o600)
os.chmod(dst, 0600)
os.chown(dst, 0, 0) os.chown(dst, 0, 0)
certificate_repository_path = os.path.join(slap_configuration_directory, 'ssl', 'partition_pki') certificate_repository_path = os.path.join(slap_conf_dir, 'ssl', 'partition_pki')
if not os.path.exists(certificate_repository_path): if not os.path.exists(certificate_repository_path):
config.logger.info ("Creating directory: %s" % certificate_repository_path) config.logger.info("Creating directory: %s" % certificate_repository_path)
if not dry_run: if not dry_run:
os.mkdir(certificate_repository_path, 0711) os.mkdir(certificate_repository_path, 0o711)
# Put slapos configuration file # Put slapos configuration file
slap_configuration_file_location = os.path.join(slap_configuration_directory, slap_conf_file = os.path.join(slap_conf_dir, 'slapos.cfg')
'slapos.cfg') config.logger.info("Creating slap configuration: %s" % slap_conf_file)
config.logger.info ("Creating slap configuration: %s"
% slap_configuration_file_location)
# Get example configuration file # Get example configuration file
slapos_cfg_example = get_slapos_conf_example() slapos_cfg_example = get_slapos_conf_example()
configuration_example_parser = ConfigParser.RawConfigParser() conf_parser = ConfigParser.RawConfigParser()
configuration_example_parser.read(slapos_cfg_example) conf_parser.read(slapos_cfg_example)
os.remove(slapos_cfg_example) os.remove(slapos_cfg_example)
# prepare slapos section for section, key, value in [
slaposconfig = dict( ('slapos', 'computer_id', config.computer_id),
computer_id=config.computer_id, master_url=config.master_url, ('slapos', 'master_url', config.master_url),
key_file=key_file, cert_file=cert_file, ('slapos', 'key_file', key_file),
certificate_repository_path=certificate_repository_path) ('slapos', 'cert_file', cert_file),
for key in slaposconfig: ('slapos', 'certificate_repository_path', certificate_repository_path),
configuration_example_parser.set('slapos', key, slaposconfig[key]) ('slapformat', 'interface_name', config.interface_name),
('slapformat', 'ipv4_local_network', config.ipv4_local_network),
# prepare slapformat ('slapformat', 'partition_amount', config.partition_number),
slapformatconfig = dict( ('slapformat', 'create_tap', config.create_tap)
interface_name=config.interface_name, ]:
ipv4_local_network=config.ipv4_local_network, conf_parser.set(section, key, value)
partition_amount=config.partition_number,
create_tap=config.create_tap if config.ipv6_interface:
) conf_parser.set('slapformat', 'ipv6_interface', config.ipv6_interface)
for key in slapformatconfig :
configuration_example_parser.set('slapformat', key, slapformatconfig[key])
if not config.ipv6_interface == '':
configuration_example_parser.set('slapformat',
'ipv6_interface',
config.ipv6_interface)
if not dry_run: if not dry_run:
slap_configuration_file = open(slap_configuration_file_location, "w") with open(slap_conf_file, 'w') as fout:
configuration_example_parser.write(slap_configuration_file) conf_parser.write(fout)
config.logger.info ("SlapOS configuration: DONE") config.logger.info("SlapOS configuration: DONE")
# Class containing all parameters needed for configuration class RegisterConfig(object):
class Config: """
def setConfig(self, option_dict, node_name): Class containing all parameters needed for configuration
"""
def __init__(self, logger):
self.logger = logger
def setConfig(self, options):
""" """
Set options given by parameters. Set options given by parameters.
""" """
# Set options parameters # Set options parameters
for option, value in option_dict.__dict__.items(): for option, value in options.__dict__.items():
setattr(self, option, value) setattr(self, option, value)
self.node_name = node_name
# Define logger for register
self.logger = logging.getLogger('Register')
self.logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
self.ch = logging.StreamHandler()
self.ch.setLevel(logging.INFO)
# create formatter
self.formatter = logging.Formatter('%(levelname)s - %(message)s')
# add formatter to ch
self.ch.setFormatter(self.formatter)
# add ch to logger
self.logger.addHandler(self.ch)
def COMPConfig(self, slapos_configuration, computer_id, certificate, key): def COMPConfig(self, slapos_configuration, computer_id, certificate, key):
self.slapos_configuration = slapos_configuration self.slapos_configuration = slapos_configuration
...@@ -265,38 +222,39 @@ class Config: ...@@ -265,38 +222,39 @@ class Config:
self.logger.debug("Ipv4 sub network: %s" % self.ipv4_local_network) self.logger.debug("Ipv4 sub network: %s" % self.ipv4_local_network)
self.logger.debug("Ipv6 Interface: %s" %self.ipv6_interface) self.logger.debug("Ipv6 Interface: %s" %self.ipv6_interface)
def register(config):
def gen_auth(config):
ask = True
if config.login:
if config.password:
yield config.login, config.password
ask = False
else:
yield config.login, getpass.getpass()
while ask:
yield raw_input('SlapOS Master Login: '), getpass.getpass()
def do_register(config):
"""Register new computer on SlapOS Master and generate slapos.cfg""" """Register new computer on SlapOS Master and generate slapos.cfg"""
# Get User identification and check them
if config.login == None : for login, password in gen_auth(config):
while True : if check_credentials(config.master_url_web, login, password):
print ("Please enter your SlapOS Master login") break
user_id = get_login() config.logger.warning('Wrong login/password')
if check_login(user_id, config.master_url_web):
break
config.logger.warning ("Wrong login/password")
else: else:
if config.password == None : return 1
config.password = getpass.getpass()
user_id = base64.encodestring('%s:%s' % (config.login, config.password))[:-1]
if not check_login(user_id, config.master_url_web):
config.logger.error ("Wrong login/password")
return 1
# Get source code of page having certificate and key # Get source code of page having certificate and key
certificate_key = get_certificates(user_id, config.node_name, config.master_url_web) certificate_key = get_certificates(config.master_url_web, config.node_name, login, password)
# Parse certificate and key and get computer id # Parse certificate and key and get computer id
certificate_key = parse_certificates(certificate_key) certificate, key = parse_certificates(certificate_key)
certificate = certificate_key[0]
key = certificate_key[1]
COMP = get_computer_name(certificate) COMP = get_computer_name(certificate)
# Getting configuration parameters # Getting configuration parameters
slapos_configuration = '/etc/opt/slapos/' config.COMPConfig(slapos_configuration='/etc/opt/slapos/',
config.COMPConfig(slapos_configuration=slapos_configuration, computer_id=COMP,
computer_id=COMP, certificate=certificate,
certificate = certificate, key=key)
key = key
)
# Save former configuration # Save former configuration
if not config.dry_run: if not config.dry_run:
save_former_config(config) save_former_config(config)
...@@ -316,37 +274,35 @@ def main(): ...@@ -316,37 +274,35 @@ def main():
help='Name of the node') help='Name of the node')
ap.add_argument('--interface-name', ap.add_argument('--interface-name',
help="Interface name to access internet", help='Interface name to access internet',
default='eth0') default='eth0')
ap.add_argument('--master-url', ap.add_argument('--master-url',
help="URL of SlapOS master", help='URL of SlapOS master',
default='https://slap.vifib.com') default='https://slap.vifib.com')
ap.add_argument('--master-url-web', ap.add_argument('--master-url-web',
help="URL of SlapOS Master webservice to register certificates", help='URL of SlapOS Master webservice to register certificates',
default='https://www.slapos.org') default='https://www.slapos.org')
ap.add_argument('--partition-number', ap.add_argument('--partition-number',
help="Number of partition on computer", help='Number of partition on computer',
default='10', default='10',
type=int) type=int)
ap.add_argument('--ipv4-local-network', ap.add_argument('--ipv4-local-network',
help="Base of ipv4 local network", help='Base of ipv4 local network',
default='10.0.0.0/16') default='10.0.0.0/16')
ap.add_argument('--ipv6-interface', ap.add_argument('--ipv6-interface',
help="Interface name to get ipv6", help='Interface name to get ipv6',
default='') default='')
ap.add_argument('--login', ap.add_argument('--login',
help="User login on SlapOS Master webservice", help='User login on SlapOS Master webservice')
default=None)
ap.add_argument('--password', ap.add_argument('--password',
help="User password on SlapOs Master webservice", help='User password on SlapOs Master webservice')
default=None)
ap.add_argument('-t', '--create-tap', ap.add_argument('-t', '--create-tap',
help='Will trigger creation of one virtual "tap" interface per ' help='Will trigger creation of one virtual "tap" interface per '
...@@ -354,33 +310,31 @@ def main(): ...@@ -354,33 +310,31 @@ def main():
'primary interface to be a bridge. defaults to false. ' 'primary interface to be a bridge. defaults to false. '
'Needed to host virtual machines.', 'Needed to host virtual machines.',
default=False, default=False,
action="store_true") action='store_true')
ap.add_argument('-n', '--dry-run', ap.add_argument('-n', '--dry-run',
help="Simulate the execution steps", help='Simulate the execution steps',
default=False, default=False,
action="store_true") action='store_true')
options = ap.parse_args() options = ap.parse_args()
if options.password and not options.login:
ap.error('Please enter your login with your password')
if options.password != None and options.login == None : logger = logging.getLogger('Register')
self.error("Please enter your login with your password") handler = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s'))
logger.addHandler(handler)
try: try:
config = Config() config = RegisterConfig(logger=logger)
config.setConfig(options, options.node_name) config.setConfig(options)
return_code = register(config) return_code = do_register(config)
except UsageError, err:
print >> sys.stderr, err.msg
print >> sys.stderr, "For help use --help"
return_code = 16
except ExecError, err:
print >> sys.stderr, err.msg
return_code = 16
except SystemExit, err: except SystemExit, err:
# Catch exception raise by optparse # Catch exception raised by optparse
return_code = err return_code = err
sys.exit(return_code) sys.exit(return_code)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment