|
@@ -0,0 +1,219 @@
|
|
1
|
+#!/usr/bin/env python3
|
|
2
|
+#-*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+INSTANCES_ABSPATH="/tmp/lodel2_instances"
|
|
6
|
+STORE_FILE='./instances.json'
|
|
7
|
+CREATION_SCRIPT='../scripts/create_instance.sh'
|
|
8
|
+INSTALL_TPL = './slim_ressources/slim_install_model'
|
|
9
|
+EMFILE = './slim_ressources/emfile.pickle'
|
|
10
|
+
|
|
11
|
+import os, os.path
|
|
12
|
+import sys
|
|
13
|
+import shutil
|
|
14
|
+import argparse
|
|
15
|
+import logging
|
|
16
|
+import json
|
|
17
|
+
|
|
18
|
+logging.basicConfig(level=logging.INFO)
|
|
19
|
+
|
|
20
|
+CREATION_SCRIPT=os.path.join(os.path.dirname(__file__), CREATION_SCRIPT)
|
|
21
|
+STORE_FILE=os.path.join(os.path.dirname(__file__), STORE_FILE)
|
|
22
|
+INSTALL_TPL=os.path.join(os.path.dirname(__file__), INSTALL_TPL)
|
|
23
|
+EMFILE=os.path.join(os.path.dirname(__file__), EMFILE)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+#STORE_FILE syntax :
|
|
27
|
+#
|
|
28
|
+#First level keys are instances names, their values are dict with following
|
|
29
|
+#informations :
|
|
30
|
+# - path
|
|
31
|
+#
|
|
32
|
+
|
|
33
|
+##@brief Run 'make %target%' for each instances given in names
|
|
34
|
+#@param target str : make target
|
|
35
|
+#@param names list : list of instance name
|
|
36
|
+def run_make(target, names):
|
|
37
|
+ validate_names(names)
|
|
38
|
+ store_datas = get_store_datas()
|
|
39
|
+ cwd = os.getcwd()
|
|
40
|
+ for name in [n for n in store_datas if n in names]:
|
|
41
|
+ datas = store_datas[name]
|
|
42
|
+ logging.info("Running 'make %s' for '%s' in %s" % (
|
|
43
|
+ target, name, datas['path']))
|
|
44
|
+ os.chdir(datas['path'])
|
|
45
|
+ os.system('make %s' % target)
|
|
46
|
+ os.chdir(cwd)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+##@brief If the name is not valid raise
|
|
50
|
+def name_is_valid(name):
|
|
51
|
+ allowed_chars = [chr(i) for i in range(ord('a'), ord('z')+1)]
|
|
52
|
+ allowed_chars += [chr(i) for i in range(ord('A'), ord('Z')+1)]
|
|
53
|
+ allowed_chars += [chr(i) for i in range(ord('0'), ord('9')+1)]
|
|
54
|
+ allowed_chars += ['_']
|
|
55
|
+ for c in name:
|
|
56
|
+ if c not in allowed_chars:
|
|
57
|
+ raise RuntimeError("Allowed characters for instance name are \
|
|
58
|
+lower&upper alphanum and '_'. Name '%s' is invalid" % name)
|
|
59
|
+
|
|
60
|
+##@brief Create a new instance
|
|
61
|
+#@param name str : the instance name
|
|
62
|
+def new_instance(name):
|
|
63
|
+ name_is_valid(name)
|
|
64
|
+ store_datas = get_store_datas()
|
|
65
|
+ if name in store_datas:
|
|
66
|
+ logging.error("An instance named '%s' already exists" % name)
|
|
67
|
+ exit(1)
|
|
68
|
+ if not os.path.isdir(INSTANCES_ABSPATH):
|
|
69
|
+ logging.info("Instances directory '%s' don't exists, creating it")
|
|
70
|
+ os.mkdir(INSTANCES_ABSPATH)
|
|
71
|
+ instance_path = os.path.join(INSTANCES_ABSPATH, name)
|
|
72
|
+ creation_cmd = '{script} "{name}" "{path}" "{install_tpl}" \
|
|
73
|
+"{emfile}"'.format(
|
|
74
|
+ script = CREATION_SCRIPT,
|
|
75
|
+ name = name,
|
|
76
|
+ path = instance_path,
|
|
77
|
+ install_tpl = INSTALL_TPL,
|
|
78
|
+ emfile = EMFILE)
|
|
79
|
+ res = os.system(creation_cmd)
|
|
80
|
+ if res != 0:
|
|
81
|
+ logging.error("Creation script fails")
|
|
82
|
+ exit(res)
|
|
83
|
+ #storing new instance
|
|
84
|
+ store_datas[name] = {'path': instance_path}
|
|
85
|
+ save_datas(store_datas)
|
|
86
|
+
|
|
87
|
+##@brief Delete an instance
|
|
88
|
+#@param name str : the instance name
|
|
89
|
+def delete_instance(name):
|
|
90
|
+ store_datas = get_store_datas()
|
|
91
|
+ logging.warning("Deleting instance %s" % name)
|
|
92
|
+ logging.info("Deleting instance folder %s" % store_datas[name]['path'])
|
|
93
|
+ shutil.rmtree(store_datas[name]['path'])
|
|
94
|
+ logging.info("Deleting instance from json store file")
|
|
95
|
+ del(store_datas[name])
|
|
96
|
+ save_datas(store_datas)
|
|
97
|
+
|
|
98
|
+##@brief returns stored datas
|
|
99
|
+def get_store_datas():
|
|
100
|
+ if not os.path.isfile(STORE_FILE):
|
|
101
|
+ return dict()
|
|
102
|
+ else:
|
|
103
|
+ with open(STORE_FILE, 'r') as sfp:
|
|
104
|
+ datas = json.load(sfp)
|
|
105
|
+ return datas
|
|
106
|
+
|
|
107
|
+##@brief Checks names validity and exit if fails
|
|
108
|
+def validate_names(names):
|
|
109
|
+ store_datas = get_store_datas()
|
|
110
|
+ invalid = [ n for n in names if n not in store_datas]
|
|
111
|
+ if len(invalid) > 0:
|
|
112
|
+ print("Following names are not existing instance :", file=sys.stderr)
|
|
113
|
+ for name in invalid:
|
|
114
|
+ print("\t%s" % name, file=sys.stderr)
|
|
115
|
+ exit(1)
|
|
116
|
+
|
|
117
|
+##@brief Check if instance are specified
|
|
118
|
+def get_specified(args):
|
|
119
|
+ if args.all:
|
|
120
|
+ names = list(get_store_datas().keys())
|
|
121
|
+ elif args.name is not None:
|
|
122
|
+ names = args.name
|
|
123
|
+ else:
|
|
124
|
+ names = None
|
|
125
|
+ return names
|
|
126
|
+
|
|
127
|
+##@brief Saves store datas
|
|
128
|
+def save_datas(datas):
|
|
129
|
+ with open(STORE_FILE, 'w+') as sfp:
|
|
130
|
+ json.dump(datas, sfp)
|
|
131
|
+
|
|
132
|
+##@brief Print the list of instances and exit
|
|
133
|
+def list_instances(verbosity):
|
|
134
|
+ if not os.path.isfile(STORE_FILE):
|
|
135
|
+ print("No store file, no instances are existing. Exiting...",
|
|
136
|
+ file=sys.stderr)
|
|
137
|
+ exit(0)
|
|
138
|
+ print('Instances list :')
|
|
139
|
+ exit(0)
|
|
140
|
+
|
|
141
|
+##@brief Returns instanciated parser
|
|
142
|
+def get_parser():
|
|
143
|
+ parser = argparse.ArgumentParser(
|
|
144
|
+ description='SLIM (Simple Lodel Instance Manager.)')
|
|
145
|
+ selector = parser.add_argument_group('Instances selectors')
|
|
146
|
+ actions = parser.add_argument_group('Instances actions')
|
|
147
|
+ parser.add_argument('-l', '--list',
|
|
148
|
+ help='list existing instances and exit', action='store_const',
|
|
149
|
+ const=True, default=False)
|
|
150
|
+ parser.add_argument('-v', '--verbose', action='count')
|
|
151
|
+ actions.add_argument('-c', '--create', action='store_const',
|
|
152
|
+ default=False, const=True,
|
|
153
|
+ help="Create a new instance with given name (see -n --name)")
|
|
154
|
+ actions.add_argument('-d', '--delete', action='store_const',
|
|
155
|
+ default=False, const=True,
|
|
156
|
+ help="Delete an instance with given name (see -n --name)")
|
|
157
|
+ actions.add_argument('-e', '--edit-config', action='store_const',
|
|
158
|
+ default=False, const=True,
|
|
159
|
+ help='Edit configuration of specified instance')
|
|
160
|
+ selector.add_argument('-a', '--all', action='store_const',
|
|
161
|
+ default=False, const=True,
|
|
162
|
+ help='Select all instances')
|
|
163
|
+ selector.add_argument('-n', '--name', metavar='NAME', type=str, nargs='*',
|
|
164
|
+ help="Specify an instance name")
|
|
165
|
+ actions.add_argument('--stop', action='store_const',
|
|
166
|
+ default=False, const=True, help="Stop instances")
|
|
167
|
+ actions.add_argument('--start', action='store_const',
|
|
168
|
+ default=False, const=True, help="Start instances")
|
|
169
|
+ actions.add_argument('-m', '--make', metavar='TARGET', type=str,
|
|
170
|
+ nargs="?", default='not',
|
|
171
|
+ help='Run make for selected instances')
|
|
172
|
+ return parser
|
|
173
|
+
|
|
174
|
+if __name__ == '__main__':
|
|
175
|
+ parser = get_parser()
|
|
176
|
+ args = parser.parse_args()
|
|
177
|
+ if args.list:
|
|
178
|
+ list_instances(args.verbose)
|
|
179
|
+ elif args.create:
|
|
180
|
+ if args.name is None:
|
|
181
|
+ parser.print_help()
|
|
182
|
+ print("\nAn instance name expected when creating an instance !",
|
|
183
|
+ file=sys.stderr)
|
|
184
|
+ exit(1)
|
|
185
|
+ for name in args.name:
|
|
186
|
+ new_instance(name)
|
|
187
|
+ elif args.delete:
|
|
188
|
+ if args.name is None:
|
|
189
|
+ parser.print_help()
|
|
190
|
+ print("\nAn instance name expected when creating an instance !",
|
|
191
|
+ file=sys.stderr)
|
|
192
|
+ exit(1)
|
|
193
|
+ validate_names(args.name)
|
|
194
|
+ for name in args.name:
|
|
195
|
+ delete_instance(name)
|
|
196
|
+ elif args.make != 'not':
|
|
197
|
+ if args.make is None:
|
|
198
|
+ target = 'all'
|
|
199
|
+ else:
|
|
200
|
+ target = args.make
|
|
201
|
+ names = get_specified(args)
|
|
202
|
+ if names is None:
|
|
203
|
+ parser.print_help()
|
|
204
|
+ print("\nWhen using -m --make options you have to select \
|
|
205
|
+instances, either by name using -n or all using -a")
|
|
206
|
+ exit(1)
|
|
207
|
+ run_make(target, names)
|
|
208
|
+ elif args.edit_config:
|
|
209
|
+ names = get_specified(args)
|
|
210
|
+ if len(names) > 1:
|
|
211
|
+ print("\n-e --edit-config option works only when 1 instance is \
|
|
212
|
+specified")
|
|
213
|
+ validate_names(names)
|
|
214
|
+ name = names[0]
|
|
215
|
+ store_datas = get_store_datas()
|
|
216
|
+ conffile = os.path.join(store_datas[name]['path'], 'conf.d')
|
|
217
|
+ conffile = os.path.join(conffile, 'lodel2.ini')
|
|
218
|
+ os.system('editor "%s"' % conffile)
|
|
219
|
+ exit(0)
|