Skip to content

Commit e6fef39

Browse files
committed
Travis and generate config tool
1 parent 88c3146 commit e6fef39

File tree

9 files changed

+655
-0
lines changed

9 files changed

+655
-0
lines changed

.travis.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
language: python
2+
3+
python:
4+
- "2.7"
5+
6+
branches:
7+
only:
8+
- master
9+
- /^stable\d+(\.\d+)?$/
10+
11+
matrix:
12+
fast_finish: true
13+
14+
script:
15+
16+
# Run smashbox tests
17+
- ./travis/check-syntax.sh

bin/config_gen

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python
2+
3+
import sys, os.path
4+
# insert the path to cernafs based on the relative position of this scrip inside the service directory tree
5+
exeDir = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
6+
pythonDir = os.path.join(os.path.dirname(exeDir), 'python' )
7+
sys.path.insert(0, pythonDir)
8+
etcDir = os.path.join(os.path.dirname(exeDir), 'etc')
9+
defaultTemplateFile = os.path.join(etcDir, 'smashbox.conf.template')
10+
defaultOutputFile = os.path.join(etcDir, 'smashbox.conf')
11+
12+
import smashbox.configgen.generator as generator
13+
import smashbox.configgen.processors as processors
14+
from smashbox.configgen.processors_hooks import LoggingHook
15+
import logging
16+
import argparse
17+
import json
18+
19+
parser = argparse.ArgumentParser(description='Config generator for smashbox')
20+
parser.add_argument('-i', default=defaultTemplateFile, help='template file to be used', dest='input_file')
21+
parser.add_argument('-o', default=defaultOutputFile, help='output file', dest='output_file')
22+
group = parser.add_mutually_exclusive_group()
23+
group.add_argument('--no-ask', default=None, action='store_false', help='don\'t ask for required keys', dest='ask_keys')
24+
group.add_argument('--ask', default=None, action='store_true', help='ask for required keys', dest='ask_keys')
25+
parser.add_argument('-k', default=[], action='append', required=False, help='key=value pairs', dest='keys')
26+
parser.add_argument('-kt', default=[], action='append', required=False, help='key=type pairs', dest='key_types')
27+
parser.add_argument('--key-value-file', help='json file containing key-value pairs. The file format should something like {keyname: {value: value, type: type}, oc_server: {value: server.com, type: string}, oc_ssl_enable: {value: True, type: bool}}')
28+
parser.add_argument('--logfile', help='write logs in this file')
29+
args = parser.parse_args()
30+
31+
global_vars = {}
32+
local_vars = {}
33+
with open(args.input_file) as ifile:
34+
code = compile(ifile.read(), args.input_file, 'exec')
35+
exec(code, global_vars, local_vars)
36+
37+
overwrite_dict = {}
38+
39+
if args.key_value_file:
40+
with open(args.key_value_file, 'r') as f:
41+
data = json.load(f)
42+
if type(data) is dict:
43+
for data_element in data:
44+
key = data_element
45+
value = str(data[data_element]['value'])
46+
if 'type' in data[data_element]:
47+
value = processors.convert_string_to_type(value, data[data_element]['type'])
48+
overwrite_dict[key] = value
49+
50+
# convert the keys argument to a dictionary
51+
key_list = [item.split('=', 1) for item in args.keys]
52+
key_dict = dict(key_list)
53+
54+
# convert the key_types to [[key, type],[key, type]] and change the type of the values
55+
key_type_list = [item.split('=', 1) for item in args.key_types]
56+
for keytype in key_type_list:
57+
if keytype[0] in key_dict:
58+
key_dict[keytype[0]] = processors.convert_string_to_type(key_dict[keytype[0]], keytype[1])
59+
overwrite_dict.update(key_dict)
60+
61+
config_generator = generator.Generator()
62+
config_generator.set_processors_from_data(local_vars['_configgen'])
63+
64+
if args.ask_keys is not None:
65+
processor = config_generator.get_processor_by_name('RequiredKeysProcessor')
66+
if processor is not None:
67+
processor.set_ask(args.ask_keys)
68+
69+
if overwrite_dict:
70+
# we need to overwrite keys
71+
processor2 = config_generator.get_processor_by_name('OverwritterProcessor')
72+
if processor2 is not None:
73+
processor2.set_dict_to_merge(overwrite_dict)
74+
75+
# setup logging for each processor
76+
if args.logfile:
77+
logging.basicConfig(level=logging.NOTSET, format='%(asctime)-15s %(levelname)s %(name)s : %(message)s', filename=args.logfile)
78+
for p in config_generator.get_processor_list():
79+
processor_name = p.get_name()
80+
logger = logging.getLogger('%s.%s' % (__name__, processor_name))
81+
p.register_observer('logger', LoggingHook(logger, logging.INFO))
82+
83+
logging.getLogger(__name__).info('ready to start the generation')
84+
85+
# generate the config file
86+
config_generator.process_data_to_file(local_vars, args.output_file)
87+
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#
2+
# The _open_SmashBox Project.
3+
#
4+
# Author: Jakub T. Moscicki, CERN, 2013
5+
# License: AGPL
6+
#
7+
# this is the main config file template: copy to smashbox.conf and adjust the settings
8+
#
9+
# this template should work without changes if you are running your tests directly on the owncloud application server
10+
#
11+
12+
# this is the top directory where all local working files are kept (test working direcotires, test logs, test data, temporary filesets, ..)
13+
smashdir = "~/smashdir"
14+
15+
# name of the account used for testing
16+
# if None then account name is chosen automatically (based on the test name)
17+
oc_account_name=None
18+
19+
# default number of users for tests involving multiple users (user number is appended to the oc_account_name)
20+
# this only applies to the tests involving multiple users
21+
oc_number_test_users=3
22+
23+
# name of the group used for testing
24+
oc_group_name=None
25+
26+
# default number of groups for tests involving multiple groups (group number is appended to the oc_group_name)
27+
# this only applies to the tests involving multiple groups
28+
oc_number_test_groups=1
29+
30+
# password for test accounts: all test account will have the same password
31+
# if not set then it's an error
32+
oc_account_password="demo"
33+
34+
# owncloud test server
35+
# if left blank or "localhost" then the real hostname of the localhost will be set
36+
oc_server = ''
37+
38+
39+
# root of the owncloud installation as visible in the URL
40+
oc_root = 'owncloud'
41+
42+
# webdav endpoint URI within the oc_server
43+
import os.path
44+
oc_webdav_endpoint = os.path.join(oc_root,'remote.php/webdav') # standard owncloud server
45+
46+
# target folder on the server (this may not be compatible with all tests)
47+
oc_server_folder = ''
48+
49+
# should we use protocols with SSL (https, ownclouds)
50+
oc_ssl_enabled = True
51+
52+
# how to invoke shell commands on the server
53+
# for localhost there is no problem - leave it blank
54+
# for remote host it may be set like this: "ssh -t -l root $oc_server"
55+
# note: configure ssh for passwordless login
56+
# note: -t option is to make it possible to run sudo
57+
oc_server_shell_cmd = ""
58+
59+
# Data directory on the owncloud server.
60+
#
61+
oc_server_datadirectory = os.path.join('/var/www/html',oc_root, 'data')
62+
63+
# a path to server side tools (create_user.php, ...)
64+
#
65+
# it may be specified as relative path "dir" and then resolves to
66+
# <smashbox>/dir where <smashbox> is the top-level of of the tree
67+
# containing THIS configuration file
68+
#
69+
70+
oc_server_tools_path = "server-tools"
71+
72+
# a path to ocsync command with options
73+
# this path should work for all client hosts
74+
#
75+
# it may be specified as relative path "dir" and then resolves to
76+
# <smashbox>/dir where <smashbox> is the top-level of of the tree
77+
# containing THIS configuration file
78+
#
79+
oc_sync_cmd = "client/build/mirall/bin/owncloudcmd --trust"
80+
81+
# number of times to repeat ocsync run every time
82+
oc_sync_repeat = 1
83+
84+
####################################
85+
86+
# unique identifier of your test run
87+
# if None then the runid is chosen automatically (and stored in this variable)
88+
runid = None
89+
90+
# if True then the local working directory path will have the runid added to it automatically
91+
workdir_runid_enabled=False
92+
93+
# if True then the runid will be part of the oc_account_name automatically
94+
oc_account_runid_enabled=False
95+
96+
####################################
97+
98+
# this defines the default account cleanup procedure
99+
# - "delete": delete account if exists and then create a new account with the same name
100+
# - "keep": don't delete existing account but create one if needed
101+
#
102+
# these are not implemeted yet:
103+
# - "sync_delete": delete all files via a sync run
104+
# - "webdav_delete": delete all files via webdav DELETE request
105+
# - "filesystem_delete": delete all files directly on the server's filesystem
106+
oc_account_reset_procedure = "delete"
107+
108+
# this defined the default local run directory reset procedure
109+
# - "delete": delete everything in the local run directory prior to running the test
110+
# - "keep": keep all files (from the previous run)
111+
rundir_reset_procedure = "delete"
112+
113+
web_user = "www-data"
114+
115+
oc_admin_user = "at_admin"
116+
oc_admin_password = "admin"
117+
118+
# cleanup imported namespaces
119+
del os
120+
121+
# Verbosity of curl client.
122+
# If none then verbosity is on when smashbox run in --debug mode.
123+
# set it to True or False to override
124+
#
125+
pycurl_verbose = None
126+
127+
# scp port to be used in scp commands, used primarily when copying over the server log file
128+
scp_port = 22
129+
130+
# user that can r+w the owncloud.log file (needs to be configured for passwordless login)
131+
oc_server_log_user = "www-data"
132+
133+
#
134+
# Reset the server log file and verify that no exceptions and other known errors have been logged
135+
#
136+
oc_check_server_log = False
137+
138+
from collections import OrderedDict
139+
_configgen = OrderedDict([('KeyRemoverProcessor',
140+
{'keylist': ('_configgen', 'oc_server', 'oc_ssl_enabled',
141+
'oc_admin_user', 'oc_admin_password',
142+
'oc_root', 'oc_webdav_endpoint', 'oc_server_shell_cmd',
143+
'oc_sync_cmd', 'scp_port')}),
144+
('OverwritterProcessor',
145+
{'dict_to_merge': {}}),
146+
('RequiredKeysProcessor',
147+
{'keylist': [
148+
{'name': 'oc_server', 'help_text': 'ip or hostname of the server where owncloud is located, including the port, such as "10.20.30.40:8080"'},
149+
{'name': 'oc_ssl_enabled', 'type': 'bool', 'default': False, 'help_text': 'if you access to the server through https, set this to True'},
150+
{'name': 'oc_root', 'help_text': 'the path for the url to be added after the server. To access to "http://server.com/owncloud" use "owncloud", leave it empty if you want to access to "http://server.com/"'},
151+
{'name': 'oc_webdav_endpoint', 'help_text': 'the path for the webdav endpoint. If the webdav endpoint is in "http://server.com/owncloud/remote.php/webdav" use "owncloud/remote.php/webdav"', 'default': 'remote.php/webdav'},
152+
{'name': 'oc_admin_user', 'default':'admin'},
153+
{'name': 'oc_admin_password', 'default': 'Password'},
154+
{'name': 'oc_server_shell_cmd', 'help_text': 'ssh command to connect to the server such as "ssh -t -l root <server_url>" (include the server). Leave it empty if the server is localhost'},
155+
{'name': 'scp_port', 'type': 'int', 'default': 22, 'help_text': 'port for scp commands accessing the owncloud server'},
156+
{'name': 'oc_sync_cmd', 'default': '/usr/bin/owncloudcmd --trust', 'help_text': 'owncloudcmd command. Use the absolute path to the app and any required option'},
157+
],
158+
'ask': True}),
159+
('SortProcessor', None)])
160+
del OrderedDict

python/smashbox/configgen/__init__.py

Whitespace-only changes.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import smashbox.configgen.processors as processors
2+
3+
class Generator(object):
4+
'''
5+
Class to generate configuration files.
6+
7+
You need to set a processor chain in order to process the dict-like object
8+
and write it into a file. If no processor is set, it will write the same object
9+
10+
Result may vary depending the processor chain being used
11+
'''
12+
def __init__(self, processor_list = None):
13+
'''
14+
Initialize the object with the processor chain set, or with an empty chain
15+
if None
16+
'''
17+
self.processor_list = [] if processor_list == None else processor_list
18+
19+
def insert_processor(self, i, processor):
20+
'''
21+
Insert a new processor in the "i" position
22+
Check list.insert for details
23+
'''
24+
self.processor_list.insert(i, processor)
25+
26+
def append_processor(self, processor):
27+
'''
28+
Append the processor to the end of the chain
29+
'''
30+
self.processor_list.append(processor)
31+
32+
def get_processor_list(self):
33+
'''
34+
Get the processor list / chain
35+
'''
36+
return self.processor_list
37+
38+
def get_processor_by_name(self, name):
39+
'''
40+
Get the processor by name or None if it's not found
41+
'''
42+
for p in self.processor_list:
43+
if p.get_name() == name:
44+
return p
45+
46+
def process_dict(self, local_dict):
47+
'''
48+
Process the dictionary. It will go through all the process chain and it will be
49+
returned after that.
50+
'''
51+
for p in self.processor_list:
52+
local_dict = p.do_process(local_dict)
53+
return local_dict
54+
55+
def write_dict(self, output_file, local_dict):
56+
'''
57+
Write the dictionary into a file. It will be readable by using the execfile
58+
function, which should be the same or similar format that the smashbox.conf.template
59+
file has, and MUST be a valid smashbox.conf file
60+
'''
61+
with open(output_file, 'w') as f:
62+
for key in local_dict:
63+
f.write('%s = %s\n' % (key, repr(local_dict[key])))
64+
65+
def generate_new_config(self, input_file, output_file):
66+
'''
67+
Generate a new configuration file from the input_file. The input file should be
68+
similar to the smashbox.conf.template. The processor chain must be set before
69+
calling this function
70+
'''
71+
input_globals = {}
72+
input_locals = {}
73+
execfile(input_file, input_globals, input_locals)
74+
75+
input_locals = self.process_dict(input_locals)
76+
self.write_dict(output_file, input_locals)
77+
78+
def set_processors_from_data(self, processor_data):
79+
'''
80+
Set the processor chain based on the data passed as parameter. Check the
81+
_configgen variable in the smashbox.conf.template for working data
82+
83+
The processor_data should be a dictionary-like. Due to the order of the processor
84+
matters, an OrderedDict is recommended.
85+
The keys of the dictionary are
86+
the class name of the processor that will be used (from the
87+
smashbox.configgen.processors module). Currently there are only 4 processors available.
88+
The value for the key should also be a dictionary to initialize the processor. Only
89+
one parameter will be passed, that's why a dictionary is recommended, although
90+
what you must pass depends on the specific processor.
91+
'''
92+
for key in processor_data:
93+
if hasattr(processors, key):
94+
processor_class = getattr(processors, key)
95+
if not issubclass(processor_class, processors.BasicProcessor):
96+
continue
97+
values = processor_data[key]
98+
processor = processor_class(values)
99+
self.append_processor(processor)
100+
else:
101+
pass
102+
103+
def process_data_to_file(self, data, output_file):
104+
'''
105+
Process the data passed as parameter through the chain and write the result
106+
to the file
107+
'''
108+
data_to_output = self.process_dict(data)
109+
self.write_dict(output_file, data_to_output)
110+

0 commit comments

Comments
 (0)