Commit 31edda67 authored by Marco Mariani's avatar Marco Mariani

Merge remote-tracking branch 'origin/master' into cliff

parents d8410ba1 d1e2cc5c
......@@ -10,6 +10,7 @@ New features:
* Initial windows support. [Jondy Zhao]
* Support new/changed parameters in command line tools, defined in documentation. [Marco Mariani]
* Register: support for one-time authentication token. [Marco Mariani]
* New command: "slapos configure client" [Marco Mariani]
Major Improvements:
......
......@@ -173,18 +173,25 @@ node register
.. program-output:: python slapos help node register
If login is not provided, asks for user's SlapOS Master account then password.
This will register the current node, and generate the SlapOS configuration file.
The command requires an authentication token, either provided as an argument,
or given at the interactive prompt.
Go to the SlapOS Master web page, click "My Space", then "My Account", then
"Generate a computer security token".
A token is valid for a single ``node resister`` command and will expire after one day.
The deprecated ``--login`` and ``--password`` options can be used with old SlapOS servers
that have no support for the token.
Node will register itself, if not already done, to the SlapOS Master defined in
configuration file, and will generate SlapOS configuration file.
..
XXX-Cedric should be like this: If desired node name is already taken, will raise an error.
XXX-Cedric: --master-url-web url will disappear in REST API. Currently, "register" uses
SlapOS master web URL to register computer, so it needs the web URL (like http://www.slapos.org)
If Node is already registered (:file:`slapos.cfg` and certificate already present), issues a warning,
backups the original configuration and creates a new one.
If the Node is already registered (:file:`slapos.cfg` and certificate are already present), the command
issues a warning, backups the original configuration and creates a new one.
..
XXX-Cedric should check for IPv6 in selected interface
......@@ -326,6 +333,23 @@ node supervisord
SlapOS Miscellaneous commands
-----------------------------
configure client
~~~~~~~~~~~~~~~~
.. program-output:: python slapos help configure client
This creates a client configuration file, and downloads a certificate + key pair
from the SlapOS Master. They will be used for all the "slapos client" commands.
The command requires an authentication token, either provided as an argument,
or given at the interactive prompt.
Go to the SlapOS Master web page, click "My Space", then "My Account", then
"Generate a credential security token".
A token is valid for a single `configure client` command and will expire after one day.
cache lookup
~~~~~~~~~~~~
......
......@@ -61,10 +61,12 @@ class SoftwareInstance(Item):
key = element.get('id').encode("UTF-8")
value = result_dict.get(key, None)
if value is not None:
value = value + ' ' + element.text
value = (value + ' ' + element.text)
else:
value = element.text
result_dict[key] = value.encode("UTF-8")
if value is not None:
value = value.encode("UTF-8")
result_dict[key] = value
return result_dict
security.declareProtected(Permissions.AccessContentsInformation,
......
287
\ No newline at end of file
289
\ No newline at end of file
......@@ -54,6 +54,7 @@ setup(name=name,
),},
tests_require=[
'pyflakes',
'mock'
],
zip_safe=False, # proxy depends on Flask, which has issues with
# accessing templates
......@@ -91,6 +92,7 @@ setup(name=name,
'node instance = slapos.cli.slapgrid:InstanceCommand',
# SlapOS client commands
'console = slapos.cli.console:ConsoleCommand',
'configure client = slapos.cli.configure_client:ConfigureClientCommand',
'proxy start = slapos.cli.proxy_start:ProxyStartCommand',
'proxy show = slapos.cli.proxy_show:ProxyShowCommand',
'supply = slapos.cli.supply:SupplyCommand',
......
......@@ -28,6 +28,13 @@ class ConfigCommand(Command):
(self.default_config_var, self.default_config_path))
return ap
def config_path(self, args):
if args.cfg:
cfg_path = args.cfg
else:
cfg_path = os.environ.get(self.default_config_var, self.default_config_path)
return os.path.expanduser(cfg_path)
def fetch_config(self, args):
"""
Returns a configuration object if file exists/readable/valid,
......@@ -36,14 +43,9 @@ class ConfigCommand(Command):
and will clearly show what is wrong with the file.
"""
if args.cfg:
cfg_path = args.cfg
else:
cfg_path = os.environ.get(self.default_config_var, self.default_config_path)
cfg_path = os.path.expanduser(cfg_path)
cfg_path = self.config_path(args)
self.log.debug('Loading config: %s' % cfg_path)
self.log.debug('Loading config: %s', cfg_path)
if not os.path.exists(cfg_path):
raise ConfigError('Configuration file does not exist: %s' % cfg_path)
......
# -*- coding: utf-8 -*-
import logging
import re
import os
import sys
import requests
from slapos.cli.config import ClientConfigCommand
from slapos.util import mkdir_p, parse_certificate_key_pair
class ConfigureClientCommand(ClientConfigCommand):
"""
register a node in the SlapOS cloud
"""
log = logging.getLogger('configure-client')
def get_parser(self, prog_name):
ap = super(ConfigureClientCommand, self).get_parser(prog_name)
ap.add_argument('--master-url',
default='https://slap.vifib.com',
help='URL of SlapOS Master REST API'
' (default: %(default)s)')
ap.add_argument('--master-url-web',
default='https://www.slapos.org',
help='URL of SlapOS Master webservice to register certificates'
' (default: %(default)s)')
ap.add_argument('--token',
help="SlapOS 'credential security' authentication token "
"(use '--token ask' for interactive prompt)")
return ap
def take_action(self, args):
do_configure_client(logger=self.log,
master_url_web=args.master_url_web,
token=args.token,
config_path=self.config_path(args),
master_url=args.master_url)
def get_certificate_key_pair(logger, master_url_web, token):
url = '/'.join([master_url_web, 'myspace/my_account/request-a-certificate'])
req = requests.post('/'.join([master_url_web, 'myspace/my_account/request-a-certificate/WebSection_requestNewCertificate']),
data={},
headers={'X-Access-Token': token},
verify=False)
if req.status_code == 403:
logger.critical('Access denied to the SlapOS Master. '
'Please check the authentication token or require a new one.')
sys.exit(1)
req.raise_for_status()
return parse_certificate_key_pair(req.text)
def fetch_configuration_template():
req = requests.get('http://git.erp5.org/gitweb/slapos.core.git/blob_plain/HEAD:/slapos-client.cfg.example')
req.raise_for_status()
return req.text
def do_configure_client(logger, master_url_web, token, config_path, master_url):
while not token:
token = raw_input('Credential security token: ').strip()
# Check for existence of previous configuration, certificate or key files
# where we expect to create them. If so, ask the use to manually remove them.
if os.path.exists(config_path):
logger.critical('There is file in %s. '
'Please remove it before creating a new configuration.', config_path)
sys.exit(1)
basedir = os.path.dirname(config_path)
if not os.path.isdir(basedir):
logger.debug('Creating directory %s', basedir)
mkdir_p(basedir, mode=0o700)
cert_path = os.path.join(basedir, 'client.crt')
if os.path.exists(cert_path):
logger.critical('There is a file in %s. '
'Please remove it before creating a new certificate.', cert_path)
sys.exit(1)
key_path = os.path.join(basedir, 'client.key')
if os.path.exists(key_path):
logger.critical('There is a file in %s. '
'Please remove it before creating a new key.', key_path)
sys.exit(1)
# retrieve a template for the configuration file
cfg = fetch_configuration_template()
cfg = re.sub('master_url = .*', 'master_url = %s' % master_url, cfg)
cfg = re.sub('cert_file = .*', 'cert_file = %s' % cert_path, cfg)
cfg = re.sub('key_file = .*', 'key_file = %s' % key_path, cfg)
# retrieve and parse the certicate and key
certificate, key = get_certificate_key_pair(logger, master_url_web, token)
# write everything
with os.fdopen(os.open(config_path, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as fout:
logger.debug('Writing configuration to %s', config_path)
fout.write(cfg)
with os.fdopen(os.open(cert_path, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as fout:
logger.debug('Writing certificate to %s', cert_path)
fout.write(certificate)
with os.fdopen(os.open(key_path, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as fout:
logger.debug('Writing key to %s', key_path)
fout.write(key)
logger.info('SlapOS client configuration: DONE')
......@@ -51,9 +51,13 @@ class RegisterCommand(Command):
ap.add_argument('--ipv6-interface',
help='Interface name to get ipv6')
ap.add_argument('--login-auth',
action='store_true',
help='Force login and password authentication')
ap.add_argument('--login',
help="Your SlapOS Master login. If not provided, "
"asks it interactively, then password.")
help='Your SlapOS Master login. '
'Asks it interactively, then password.')
ap.add_argument('--password',
help='Your SlapOS Master password. If not provided, '
......@@ -61,8 +65,7 @@ class RegisterCommand(Command):
'should be avoided for security reasons.')
ap.add_argument('--token',
help="SlapOS Master authentication token "
"(use '--token ask' for interactive prompt)")
help="SlapOS 'computer security' authentication token")
ap.add_argument('-t', '--create-tap',
action='store_true',
......
......@@ -42,7 +42,6 @@ from slapos.cli_legacy.slapgrid import runSoftwareRelease as software
from slapos.cli_legacy.slapgrid import runUsageReport as report
from slapos.cli_legacy.svcbackend import supervisord
from slapos.cli_legacy.svcbackend import supervisorctl
from slapos.cli_legacy.register import main as register
from slapos.version import version
# Note: this whole file is a hack. We should better try dedicated library
......@@ -125,9 +124,7 @@ def dispatch(command, is_node_command):
sys.stderr.write('This command must be run as root.\n')
sys.exit()
if command == 'register':
call(register)
elif command == 'software':
if command == 'software':
call(software, config_path=GLOBAL_SLAPOS_CONFIGURATION,
option=['--pidfile /opt/slapos/slapgrid-sr.pid'])
elif command == 'instance':
......@@ -192,7 +189,6 @@ Client subcommands usage:
slapos console
Node subcommands usage:
slapos node
slapos node register <node-id>
slapos node software
slapos node instance
slapos node report
......
# -*- coding: utf-8 -*-
# vim: set et sts=2:
##############################################################################
#
# Copyright (c) 2012 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import argparse
import logging
import sys
from slapos.register.register import do_register, RegisterConfig
def main():
ap = argparse.ArgumentParser()
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')
args = ap.parse_args()
if args.password and not args.login:
ap.error('Please enter your login with your password')
logger = logging.getLogger('Register')
handler = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s'))
logger.addHandler(handler)
try:
conf = RegisterConfig(logger=logger)
conf.setConfig(args)
return_code = do_register(conf)
except SystemExit as exc:
return_code = exc
sys.exit(return_code)
......@@ -135,9 +135,6 @@ def merged_options(args, configp):
if options.get('all'):
options['develop'] = True
if options.get('maximum_periodicity') is not None:
options['force_periodicity'] = True
# Supervisord configuration location
if not options.get('supervisord_configuration_path'):
options['supervisord_configuration_path'] = \
......@@ -199,8 +196,7 @@ def create_slapgrid_object(options, logger):
supervisord_configuration_path=op['supervisord_configuration_path'],
buildout=op.get('buildout'),
logger=logger,
force_periodicity=op.get('force_periodicity', False),
maximum_periodicity=op.get('maximum_periodicity', 86400),
maximum_periodicity = op.get('maximum_periodicity', 86400),
key_file=op.get('key_file'),
cert_file=op.get('cert_file'),
signature_private_key_file=op.get('signature_private_key_file'),
......@@ -256,7 +252,6 @@ class Slapgrid(object):
supervisord_configuration_path,
buildout,
logger,
force_periodicity=False,
maximum_periodicity=86400,
key_file=None,
cert_file=None,
......@@ -331,7 +326,6 @@ class Slapgrid(object):
self.computer_partition_filter_list = \
computer_partition_filter_list.split(",")
self.maximum_periodicity = maximum_periodicity
self.force_periodicity = force_periodicity
def getWatchdogLine(self):
invocation_list = [WATCHDOG_PATH]
......@@ -576,15 +570,13 @@ class Slapgrid(object):
periodicity = self.maximum_periodicity
if software_path:
# Get periodicity from periodicity file if not forced
if not self.force_periodicity:
periodicity_path = os.path.join(software_path, 'periodicity')
if os.path.exists(periodicity_path):
try:
periodicity = int(open(periodicity_path).read())
except ValueError:
os.remove(periodicity_path)
self.logger.exception('')
periodicity_path = os.path.join(software_path, 'periodicity')
if os.path.exists(periodicity_path):
try:
periodicity = int(open(periodicity_path).read())
except ValueError:
os.remove(periodicity_path)
self.logger.exception('')
# Check if timestamp from server is more recent than local one.
# If not: it's not worth processing this partition (nothing has
......@@ -595,13 +587,15 @@ class Slapgrid(object):
last_runtime = int(os.path.getmtime(timestamp_path))
if timestamp:
try:
if int(timestamp) <= int(old_timestamp):
if periodicity == 0:
os.remove(timestamp_path)
elif int(timestamp) <= int(old_timestamp):
if computer_partition.getState() != COMPUTER_PARTITION_STARTED_STATE:
return
# Check periodicity, i.e if periodicity is one day, partition
# should be processed at least every day.
# Only do it for "started" instances
if int(time.time()) <= (last_runtime + periodicity):
if int(time.time()) <= (last_runtime + periodicity) or periodicity < 0:
self.logger.info('Partition already up-to-date, skipping.')
return
else:
......
......@@ -42,6 +42,8 @@ from subprocess import CalledProcessError
import requests
from slapos.util import parse_certificate_key_pair
def check_credentials(url, login, password):
"""Check if login and password are correct"""
......@@ -49,7 +51,7 @@ def check_credentials(url, login, password):
return 'Logout' in req.text
def get_certificates(logger, master_url_web, node_name, token=None, login=None, password=None):
def get_certificate_key_pair(logger, master_url_web, node_name, token=None, login=None, password=None):
"""Download certificates from SlapOS Master"""
if token:
......@@ -65,7 +67,9 @@ def get_certificates(logger, master_url_web, node_name, token=None, login=None,
# raise a readable exception if the computer name is already used,
# instead of an opaque 500 Internal Error.
# this will not work with the new API.
logger.error('The node name "%s" is already in use. Please change the name, or revoke the active certificate if you want to replace the node.' % node_name)
logger.error('The node name "%s" is already in use. '
'Please change the name, or revoke the active '
'certificate if you want to replace the node.', node_name)
sys.exit(1)
if req.status_code == 403:
......@@ -73,21 +77,16 @@ def get_certificates(logger, master_url_web, node_name, token=None, login=None,
msg = 'Please check the authentication token or require a new one.'
else:
msg = 'Please check username and password.'
logger.error('Access denied to the SlapOS Master. %s', msg)
logger.critical('Access denied to the SlapOS Master. %s', msg)
sys.exit(1)
elif not req.ok and 'NotImplementedError' in req.text and not token:
logger.critical('This SlapOS server does not support login/password '
'authentication. Please use the token.')
sys.exit(1)
else:
req.raise_for_status()
return req.text
def parse_certificates(source):
"""Parse html gotten from SlapOS Master to make certificate and key files"""
c_start = source.find("Certificate:")
c_end = source.find("</textarea>", c_start)
k_start = source.find("-----BEGIN PRIVATE KEY-----")
k_end = source.find("</textarea>", k_start)
return source[c_start:c_end], source[k_start:k_end]
return parse_certificate_key_pair(req.text)
def get_computer_name(certificate):
......@@ -115,7 +114,8 @@ def save_former_config(conf):
saved += '.1'
else:
break
conf.logger.info("Former slapos configuration detected in %s moving to %s" % (former, saved))
conf.logger.info('Former slapos configuration detected '
'in %s moving to %s', former, saved)
shutil.move(former, saved)
......@@ -147,13 +147,13 @@ def slapconfig(conf):
directory = os.path.dirname(directory)
if not os.path.exists(slap_conf_dir):
conf.logger.info("Creating directory: %s" % slap_conf_dir)
conf.logger.info('Creating directory: %s', slap_conf_dir)
if not dry_run:
os.mkdir(slap_conf_dir, 0o711)
user_certificate_repository_path = os.path.join(slap_conf_dir, 'ssl')
if not os.path.exists(user_certificate_repository_path):
conf.logger.info("Creating directory: %s" % user_certificate_repository_path)
conf.logger.info('Creating directory: %s', user_certificate_repository_path)
if not dry_run:
os.mkdir(user_certificate_repository_path, 0o711)
......@@ -163,7 +163,7 @@ def slapconfig(conf):
(conf.key, key_file),
(conf.certificate, cert_file)
]:
conf.logger.info("Copying to %r, and setting minimum privileges" % dst)
conf.logger.info('Copying to %r, and setting minimum privileges', dst)
if not dry_run:
with open(dst, 'w') as destination:
destination.write(''.join(src))
......@@ -172,13 +172,13 @@ def slapconfig(conf):
certificate_repository_path = os.path.join(slap_conf_dir, 'ssl', 'partition_pki')
if not os.path.exists(certificate_repository_path):
conf.logger.info("Creating directory: %s" % certificate_repository_path)
conf.logger.info('Creating directory: %s', certificate_repository_path)
if not dry_run:
os.mkdir(certificate_repository_path, 0o711)
# Put slapos configuration file
slap_conf_file = os.path.join(slap_conf_dir, 'slapos.cfg')
conf.logger.info("Creating slap configuration: %s" % slap_conf_file)
conf.logger.info('Creating slap configuration: %s', slap_conf_file)
# Get example configuration file
slapos_cfg_example = get_slapos_conf_example()
......@@ -206,7 +206,7 @@ def slapconfig(conf):
with open(slap_conf_file, 'w') as fout:
new_configp.write(fout)
conf.logger.info("SlapOS configuration: DONE")
conf.logger.info('SlapOS configuration: DONE')
class RegisterConfig(object):
......@@ -232,12 +232,12 @@ class RegisterConfig(object):
self.key = key
def displayUserConfig(self):
self.logger.debug("Computer Name: %s" % self.node_name)
self.logger.debug("Master URL: %s" % self.master_url)
self.logger.debug("Number of partition: %s" % self.partition_number)
self.logger.info("Using Interface %s" % self.interface_name)
self.logger.debug("Ipv4 sub network: %s" % self.ipv4_local_network)
self.logger.debug("Ipv6 Interface: %s" % self.ipv6_interface)
self.logger.debug('Computer Name: %s', self.node_name)
self.logger.debug('Master URL: %s', self.master_url)
self.logger.debug('Number of partition: %s', self.partition_number)
self.logger.info('Using Interface %s', self.interface_name)
self.logger.debug('Ipv4 sub network: %s', self.ipv4_local_network)
self.logger.debug('Ipv6 Interface: %s', self.ipv6_interface)
def gen_auth(conf):
......@@ -255,15 +255,7 @@ def gen_auth(conf):
def do_register(conf):
"""Register new computer on SlapOS Master and generate slapos.cfg"""
if conf.token == 'ask':
while True:
conf.token = raw_input('SlapOS Token: ').strip()
if conf.token:
break
if conf.token:
certificate_key = get_certificates(conf.logger, conf.master_url_web, conf.node_name, token=conf.token)
else:
if conf.login or conf.login_auth:
for login, password in gen_auth(conf):
if check_credentials(conf.master_url_web, login, password):
break
......@@ -271,16 +263,29 @@ def do_register(conf):
else:
return 1
certificate_key = get_certificates(conf.logger, conf.master_url_web, conf.node_name, login=login, password=password)
certificate, key = get_certificate_key_pair(conf.logger,
conf.master_url_web,
conf.node_name,
login=login,
password=password)
else:
while not conf.token:
conf.token = raw_input('Computer security token: ').strip()
certificate, key = get_certificate_key_pair(conf.logger,
conf.master_url_web,
conf.node_name,
token=conf.token)
# Parse certificate and key and get computer id
certificate, key = parse_certificates(certificate_key)
# get computer id
COMP = get_computer_name(certificate)
# Getting configuration parameters
conf.COMPConfig(slapos_configuration='/etc/opt/slapos/',
computer_id=COMP,
certificate=certificate,
key=key)
# Save former configuration
if not conf.dry_run:
save_former_config(conf)
......
......@@ -25,6 +25,7 @@
#
##############################################################################
from __future__ import absolute_import
import httplib
import logging
import os
......@@ -40,6 +41,7 @@ import unittest
import urlparse
import xml_marshaller
from mock import patch
import slapos.slap.slap
import slapos.grid.utils
......@@ -49,6 +51,7 @@ from slapos.grid.utils import md5digest
from slapos.grid.watchdog import Watchdog, getWatchdogID
from slapos.grid import SlapObject
dummylogger = logging.getLogger()
......@@ -232,13 +235,13 @@ class MasterMixin(BasicMixin):
def _patchHttplib(self):
"""Overrides httplib"""
import mock.httplib
import slapos.tests.mock.httplib
self.saved_httplib = {}
for fake in vars(mock.httplib):
for fake in vars(slapos.tests.mock.httplib):
self.saved_httplib[fake] = getattr(httplib, fake, None)
setattr(httplib, fake, getattr(mock.httplib, fake))
setattr(httplib, fake, getattr(slapos.tests.mock.httplib, fake))
def _unpatchHttplib(self):
"""Restores httplib overriding"""
......@@ -1026,7 +1029,6 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
instance.timestamp = timestamp
instance.requested_state = 'started'
instance.software.setPeriodicity(1)
self.grid.force_periodicity = True
self.launchSlapgrid()
partition = os.path.join(self.instance_root, '0')
......@@ -1042,36 +1044,7 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
self.assertItemsEqual(os.listdir(partition),
['.timestamp', 'buildout.cfg', 'software_release', 'worked'])
def test_partition_periodicity_is_not_overloaded_if_forced(self):
"""
If periodicity file in software directory but periodicity is forced
periodicity will be the one given by parameter
1. We set force_periodicity parameter to True
2. We put a periodicity file in the software release directory
with an unwanted periodicity
3. We process partition list and wait more than unwanted periodicity
4. We relaunch, partition should not be processed
"""
computer = ComputerForTest(self.software_root, self.instance_root)
instance = computer.instance_list[0]
timestamp = str(int(time.time()))
instance.timestamp = timestamp
instance.requested_state = 'started'
unwanted_periodicity = 2
instance.software.setPeriodicity(unwanted_periodicity)
self.grid.force_periodicity = True
self.launchSlapgrid()
time.sleep(unwanted_periodicity + 1)
self.setSlapgrid()
self.grid.force_periodicity = True
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertNotEqual(unwanted_periodicity, self.grid.maximum_periodicity)
self.assertEqual(computer.sequence,
['getFullComputerInformation', 'availableComputerPartition',
'startedComputerPartition', 'getFullComputerInformation'])
def test_one_partition_periodicity_from_file_does_not_disturb_others(self):
"""
......@@ -1197,6 +1170,49 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
last_runtime)
self.assertNotEqual(wanted_periodicity, self.grid.maximum_periodicity)
def test_one_partition_is_never_processed_when_periodicity_is_negative(self):
"""
Checks that a partition is not processed when
its periodicity is negative
1. We setup one instance and set periodicity at -1
2. We mock the install method from slapos.grid.slapgrid.Partition
3. We launch slapgrid once so that .timestamp file is created and check that install method is
indeed called (through mocked_method.called
4. We launch slapgrid anew and check that install as not been called again
"""
timestamp = str(int(time.time()))
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
instance = computer.instance_list[0]
instance.software.setPeriodicity(-1)
instance.timestamp = timestamp
with patch.object(slapos.grid.slapgrid.Partition, 'install', return_value=None) as mock_method:
self.launchSlapgrid()
self.assertTrue(mock_method.called)
self.launchSlapgrid()
self.assertEqual(mock_method.call_count, 1)
def test_one_partition_is_always_processed_when_periodicity_is_zero(self):
"""
Checks that a partition is always processed when
its periodicity is 0
1. We setup one instance and set periodicity at 0
2. We mock the install method from slapos.grid.slapgrid.Partition
3. We launch slapgrid once so that .timestamp file is created
4. We launch slapgrid anew and check that install has been called twice (one time because of the
new setup and one time because of periodicity = 0)
"""
timestamp = str(int(time.time()))
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
instance = computer.instance_list[0]
instance.software.setPeriodicity(0)
instance.timestamp = timestamp
with patch.object(slapos.grid.slapgrid.Partition, 'install', return_value=None) as mock_method:
self.launchSlapgrid()
self.launchSlapgrid()
self.assertEqual(mock_method.call_count, 2)
def test_one_partition_buildout_fail_does_not_disturb_others(self):
"""
1. We set up two instance one using a corrupted buildout
......@@ -1557,26 +1573,6 @@ class TestSlapgridArgumentTuple(SlapgridInitialization):
slapgrid_object = parser(*self.default_arg_tuple)[0]
self.assertFalse(slapgrid_object.develop)
def test_force_periodicity_if_periodicity_not_given(self):
"""
Check if not giving --maximum-periodicity triggers "force_periodicity"
option to be false.
"""
parser = parseArgumentTupleAndReturnSlapgridObject
slapgrid_object = parser(*self.default_arg_tuple)[0]
self.assertFalse(slapgrid_object.force_periodicity)
def test_force_periodicity_if_periodicity_given(self):
"""
Check if giving --maximum-periodicity triggers "force_periodicity" option.
"""
parser = parseArgumentTupleAndReturnSlapgridObject
slapgrid_object = parser('--maximum-periodicity', '40', *self.default_arg_tuple)[0]
self.assertTrue(slapgrid_object.force_periodicity)
class TestSlapgridConfigurationFile(SlapgridInitialization):
def test_upload_binary_cache_blacklist(self):
"""
Check if giving --upload-to-binary-cache-url-blacklist triggers option.
......
......@@ -27,3 +27,19 @@ def chownDirectory(path, uid, gid):
for item in items:
if not os.path.islink(os.path.join(root, item)):
os.chown(os.path.join(root, item), uid, gid)
def parse_certificate_key_pair(html):
"""
Extract (certificate, key) pair from an HTML page received by SlapOS Master.
"""
c_start = html.find("Certificate:")
c_end = html.find("</textarea>", c_start)
certificate = html[c_start:c_end]
k_start = html.find("-----BEGIN PRIVATE KEY-----")
k_end = html.find("</textarea>", k_start)
key = html[k_start:k_end]
return certificate, key
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