Merge ~gavin.lin/cc-lab-manager:add-main into cc-lab-manager:master

Proposed by Gavin Lin
Status: Merged
Approved by: Gavin Lin
Approved revision: e83324914f0fbda4313bdac902f2ea3494738591
Merged at revision: 335a4ab8693473435f127bac4fe2c539bef2f791
Proposed branch: ~gavin.lin/cc-lab-manager:add-main
Merge into: cc-lab-manager:master
Diff against target: 1512 lines (+1005/-62)
32 files modified
build/lib/cc_lab_manager/c3_db/c3_db.py (+22/-5)
build/lib/cc_lab_manager/commands/__init__.py (+0/-0)
build/lib/cc_lab_manager/commands/cc_lab_manager.py (+49/-0)
build/lib/cc_lab_manager/dhcp/__init__.py (+0/-0)
build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py (+31/-14)
build/lib/cc_lab_manager/gen_config/__init__.py (+0/-0)
build/lib/cc_lab_manager/gen_config/gen_agent_tf_config.py (+35/-25)
build/lib/cc_lab_manager/gsheet_db/__init__.py (+0/-0)
build/lib/cc_lab_manager/gsheet_db/db_gsheet.py (+7/-7)
build/lib/cc_lab_manager/maas/__init__.py (+0/-0)
build/lib/cc_lab_manager/maas/create_maas_node.py (+11/-10)
cc_lab_manager.egg-info/PKG-INFO (+11/-0)
cc_lab_manager.egg-info/SOURCES.txt (+23/-0)
cc_lab_manager.egg-info/dependency_links.txt (+1/-0)
cc_lab_manager.egg-info/entry_points.txt (+3/-0)
cc_lab_manager.egg-info/requires.txt (+4/-0)
cc_lab_manager.egg-info/top_level.txt (+1/-0)
cc_lab_manager/__init__.py (+0/-0)
cc_lab_manager/c3_db/__init__.py (+0/-0)
cc_lab_manager/c3_db/c3_db.py (+128/-0)
cc_lab_manager/commands/__init__.py (+0/-0)
cc_lab_manager/commands/cc_lab_manager.py (+49/-0)
cc_lab_manager/dhcp/__init__.py (+0/-0)
cc_lab_manager/dhcp/dhcp_config_generator.py (+96/-0)
cc_lab_manager/gen_config/__init__.py (+0/-0)
cc_lab_manager/gen_config/gen_agent_tf_config.py (+203/-0)
cc_lab_manager/gsheet_db/__init__.py (+0/-0)
cc_lab_manager/gsheet_db/db_gsheet.py (+107/-0)
cc_lab_manager/gsheet_db/gsheet_db.py (+91/-0)
cc_lab_manager/maas/__init__.py (+0/-0)
cc_lab_manager/maas/create_maas_node.py (+124/-0)
setup.py (+9/-1)
Reviewer Review Type Date Requested Status
Kevin Yeh Approve
Review via email: mp+421633@code.launchpad.net

Commit message

Combine functions into a workflow and work in a single command.

To post a comment you must log in.
Revision history for this message
Kevin Yeh (kevinyeh) wrote :

LGTM +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cc-lab-manager/c3-db/__init__.py b/build/lib/cc_lab_manager/__init__.py
2similarity index 100%
3rename from cc-lab-manager/c3-db/__init__.py
4rename to build/lib/cc_lab_manager/__init__.py
5diff --git a/cc-lab-manager/dhcp/__init__.py b/build/lib/cc_lab_manager/c3_db/__init__.py
6similarity index 100%
7rename from cc-lab-manager/dhcp/__init__.py
8rename to build/lib/cc_lab_manager/c3_db/__init__.py
9diff --git a/cc-lab-manager/c3-db/c3-db.py b/build/lib/cc_lab_manager/c3_db/c3_db.py
10similarity index 86%
11rename from cc-lab-manager/c3-db/c3-db.py
12rename to build/lib/cc_lab_manager/c3_db/c3_db.py
13index a9f6779..b9b0d7e 100644
14--- a/cc-lab-manager/c3-db/c3-db.py
15+++ b/build/lib/cc_lab_manager/c3_db/c3_db.py
16@@ -1,7 +1,9 @@
17 import sqlite3
18 import logging
19+import argparse
20 import c3.api.query as c3query
21 from c3.api.api_utils import APIQuery
22+import os
23
24 logger = logging.getLogger('cc-lab-manager')
25
26@@ -22,6 +24,8 @@ def c3_to_db(db_path):
27 for cid_db in cid_list:
28 cid = ''.join(cid_db)
29 logger.info('Processing {}...'.format(cid))
30+ if '-' not in cid or 'TEL' in cid:
31+ continue
32
33 # Query hardware info from c3
34 result = ''
35@@ -94,17 +98,30 @@ def c3_to_db(db_path):
36 hwdb.close()
37
38
39+def environ_or_required(key):
40+ if os.environ.get(key):
41+ return {'default': os.environ.get(key)}
42+ else:
43+ return {'required': True}
44+
45+
46 def main():
47+ parser = argparse.ArgumentParser()
48+ parser.add_argument('--conf', help="Config file for C3 API", default = os.environ.get('CCLM_CC_TOOL_BOX') + '/config/c3-cli/my_conf.ini')
49+ parser.add_argument('--c3user', help="C3 User Account", **environ_or_required('CCLM_C3_USERNAME'))
50+ parser.add_argument('--c3apikey', help="C3 API Key", **environ_or_required('CCLM_C3_APIKEY'))
51+ parser.add_argument('--hwdb', help="C3 API Key", **environ_or_required('CCLM_HARDWARE_DB'))
52+ args = parser.parse_args()
53+
54 ci = c3query.configuration.get_instance()
55- ci.read_configuration('my_conf.ini')
56+ ci.read_configuration(args.conf)
57
58 api = APIQuery(ci.config['C3']['URI'])
59- rparam = {"username": ci.config['C3']['UserName'],
60- "api_key": ci.config['C3']['APIKey']}
61+ rparam = {"username": args.c3user,
62+ "api_key": args.c3apikey}
63
64 c3query.api_instance.set_api_params(api, rparam)
65- hwdb = './hardware.db'
66- c3_to_db(hwdb)
67+ c3_to_db(args.hwdb)
68
69
70 if __name__ == '__main__':
71diff --git a/build/lib/cc_lab_manager/commands/__init__.py b/build/lib/cc_lab_manager/commands/__init__.py
72new file mode 100644
73index 0000000..e69de29
74--- /dev/null
75+++ b/build/lib/cc_lab_manager/commands/__init__.py
76diff --git a/build/lib/cc_lab_manager/commands/cc_lab_manager.py b/build/lib/cc_lab_manager/commands/cc_lab_manager.py
77new file mode 100644
78index 0000000..7da03df
79--- /dev/null
80+++ b/build/lib/cc_lab_manager/commands/cc_lab_manager.py
81@@ -0,0 +1,49 @@
82+import logging
83+import os
84+import sys
85+
86+from cc_lab_manager.gsheet_db import gsheet_db
87+from cc_lab_manager.gsheet_db import db_gsheet
88+from cc_lab_manager.c3_db import c3_db
89+from cc_lab_manager.dhcp import dhcp_config_generator
90+from cc_lab_manager.maas import create_maas_node
91+from cc_lab_manager.gen_config import gen_agent_tf_config
92+
93+logger = logging.getLogger('cc-lab-manager')
94+
95+def main():
96+ # Read data from the google sheet to db
97+ gsheet_db.main()
98+
99+ # Get needed information from c3
100+ c3_db.main()
101+
102+ # Generate DHCP config for each subnets
103+ # TODO: Currently these config files need to be copied
104+ # manually, then MAAS will apply it automatically
105+ dhcp_config_generator.main()
106+
107+ # Create MAAS nodes for DUTs
108+ create_maas_node.main()
109+
110+ # Create agent configs for DUTs
111+ # TODO: Currently these configs need to be push/pull
112+ # and applied manually
113+ gen_agent_tf_config.main()
114+
115+ # Write data in db back to the google sheet
116+ db_gsheet.main()
117+
118+ #ci = c3query.configuration.get_instance()
119+
120+ #api = APIQuery(ci.config['C3']['URI'])
121+ #rparam = {"username": ci.config['C3']['UserName'],
122+ # "api_key": ci.config['C3']['APIKey']}
123+
124+ #c3query.api_instance.set_api_params(api, rparam)
125+ #hwdb = './hardware.db'
126+ #c3_to_db(hwdb)
127+
128+
129+if __name__ == '__main__':
130+ main()
131diff --git a/build/lib/cc_lab_manager/dhcp/__init__.py b/build/lib/cc_lab_manager/dhcp/__init__.py
132new file mode 100644
133index 0000000..e69de29
134--- /dev/null
135+++ b/build/lib/cc_lab_manager/dhcp/__init__.py
136diff --git a/cc-lab-manager/dhcp/dhcp-config-generator.py b/build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py
137similarity index 69%
138rename from cc-lab-manager/dhcp/dhcp-config-generator.py
139rename to build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py
140index 4ea854f..e586dd4 100644
141--- a/cc-lab-manager/dhcp/dhcp-config-generator.py
142+++ b/build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py
143@@ -1,7 +1,8 @@
144 import sqlite3
145 import logging
146+import argparse
147+import os
148 from jinja2 import Template
149-import c3.api.query as c3query
150
151 logger = logging.getLogger('cc-lab-manager')
152
153@@ -38,15 +39,19 @@ host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}}-ctl {
154
155 '''
156
157- def generate_dhcp_conf(self, ip_mac_list, dhcp_conf):
158- dhcp_conf_data = ''
159+ def generate_dhcp_conf(self, ip_mac_list, dhcp_conf_path):
160+ dhcp_conf_data = {}
161 for ip_mac in ip_mac_list:
162 if ip_mac['IP'] == '' or ip_mac['Controller_IP'] == ''\
163 or ip_mac['Lab'] == '' or ip_mac['Frame'] == ''\
164 or ip_mac['Shelf'] == '' or ip_mac['Partition'] == '':
165- logger.error("Missing required information for", ip_mac['CID'])
166+ if ip_mac['CID'] == '':
167+ ip_mac_list.remove(ip_mac)
168+ else:
169+ logger.info("Missing required information for", ip_mac['CID'])
170 continue
171
172+ # Generate MAC for dummy devices or devices without fixed MAC
173 if ip_mac['MAC'] == '':
174 ip = ip_mac['IP'].split('.')
175 ip_mac['MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
176@@ -56,23 +61,35 @@ host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}}-ctl {
177 ip_mac['Controller_MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
178 f'{int(ip[2]):02x}:'
179 f'{int(ip[3]):02x}')
180+
181 tm = Template(self.dhcp_template)
182 machine_ip = tm.render(ip_mac)
183- dhcp_conf_data += machine_ip
184- with open(dhcp_conf, 'w') as out:
185- out.write(dhcp_conf_data)
186+ if ip_mac['Lab'] not in dhcp_conf_data:
187+ dhcp_conf_data[ip_mac['Lab']] = machine_ip
188+ else:
189+ dhcp_conf_data[ip_mac['Lab']] += machine_ip
190
191+ for lab_id in dhcp_conf_data:
192+ with open(os.path.join(dhcp_conf_path, 'cert-' + lab_id.lower() + '-dhcpd-pool.conf'), 'w') as out:
193+ out.write(dhcp_conf_data[lab_id])
194
195-def main():
196
197- ci = c3query.configuration.get_instance()
198- ci.read_configuration('my_conf.ini')
199+def environ_or_required(key):
200+ if os.environ.get(key):
201+ return {'default': os.environ.get(key)}
202+ else:
203+ return {'required': True}
204+
205+
206+def main():
207+ parser = argparse.ArgumentParser()
208+ parser.add_argument('--cctoolbox', help="Path to cc-tool-box for storing dhcp config", **environ_or_required('CCLM_CC_TOOL_BOX'))
209+ parser.add_argument('--hwdb', help="Hardware db to read ip mapping", **environ_or_required('CCLM_HARDWARE_DB'))
210+ args = parser.parse_args()
211
212- hwdb = ci.config['CC-LAB-MANAGER']['HWDB']
213- dhcp_conf = ci.config['CC-LAB-MANAGER']['DHCP_CONFIG']
214- ip_mac_list = read_data_from_db(hwdb)
215+ ip_mac_list = read_data_from_db(args.hwdb)
216 dm = DhcpManager()
217- dm.generate_dhcp_conf(ip_mac_list, dhcp_conf)
218+ dm.generate_dhcp_conf(ip_mac_list, os.path.join(args.cctoolbox, 'config', 'dhcp'))
219
220
221 if __name__ == '__main__':
222diff --git a/build/lib/cc_lab_manager/gen_config/__init__.py b/build/lib/cc_lab_manager/gen_config/__init__.py
223new file mode 100644
224index 0000000..e69de29
225--- /dev/null
226+++ b/build/lib/cc_lab_manager/gen_config/__init__.py
227diff --git a/cc-lab-manager/gen-config/gen-agent-tf-config.py b/build/lib/cc_lab_manager/gen_config/gen_agent_tf_config.py
228similarity index 73%
229rename from cc-lab-manager/gen-config/gen-agent-tf-config.py
230rename to build/lib/cc_lab_manager/gen_config/gen_agent_tf_config.py
231index 337fe3e..8056656 100644
232--- a/cc-lab-manager/gen-config/gen-agent-tf-config.py
233+++ b/build/lib/cc_lab_manager/gen_config/gen_agent_tf_config.py
234@@ -6,7 +6,7 @@ import argparse
235 agent_config = {"device_ip": None,
236 "secure_id": None,
237 "node_id": None,
238- "maas_user": "sru-pool",
239+ "maas_user": "cert_maas_admin",
240 "agent_name": None,
241 "node_name": None,
242 "reset_efi": "true",
243@@ -65,27 +65,32 @@ def read_data_from_db(cursor):
244 results = cursor.fetchall()
245 return list(results)
246
247-def create_agent_config_dir(db_machine_list):
248+def create_agent_config_dir(db_machine_list, cfg_path):
249
250 for machine in db_machine_list:
251
252- agent_name = "sru"+machine["cid"].replace("-","")
253+ if machine['lab'] == '':
254+ continue
255+ agent_name = "dut"+machine["cid"].replace("-","")
256 try:
257- os.makedirs("{}".format(agent_name))
258+ os.makedirs(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name))
259 except:
260 print("dir exist")
261
262-def generate_agent_config(db_machine_list):
263+def generate_agent_config(db_machine_list, cfg_path):
264
265 for machine in db_machine_list:
266
267+ if machine['lab'] == '':
268+ continue
269 agent_conf = agent_config.copy()
270- agent_name = "sru" + machine["cid"].replace("-","")
271+ agent_name = "dut" + machine["cid"].replace("-","")
272 secure_id = machine["SecureID"]
273 ip = machine["IP"]
274 node_id = machine["MAAS_Node_ID"]
275- node_name = machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
276- machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]
277+ # node_name = machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
278+ # machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]
279+ node_name = machine["cid"]
280
281 agent_conf["node_name"] = node_name.lower()
282 agent_conf["agent_name"] = agent_name
283@@ -95,7 +100,7 @@ def generate_agent_config(db_machine_list):
284 agent_conf["env"]["HEXR_DEVICE_SECURE_ID"] = secure_id
285 agent_conf["env"]["DEVICE_IP"] = ip
286
287- with open("{}/default.yaml".format(agent_name), "w+") as f:
288+ with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'), "w+") as f:
289
290 for key in agent_config:
291 if key == "env":
292@@ -108,36 +113,39 @@ def generate_agent_config(db_machine_list):
293 f.write("{key}: {value}\n".format(key=key,value=agent_conf[key]))
294
295
296-def generate_tf_config(db_machine_list):
297+def generate_tf_config(db_machine_list, cfg_path):
298
299 for machine in db_machine_list:
300+ if machine['lab'] == '':
301+ continue
302 tf_conf = tf_config.copy()
303
304- agent_name = "sru" + machine["cid"].replace("-","")
305+ agent_name = "dut" + machine["cid"].replace("-","")
306 tf_conf["agent_id"] = agent_name
307 tf_conf["execution_basedir"] = tf_conf["execution_basedir"].format(agent_name)
308 tf_conf["logging_basedir"] = tf_conf["logging_basedir"].format(agent_name)
309 tf_conf["results_basedir"] = tf_conf["results_basedir"].format(agent_name)
310
311- with open("{}/testflinger-agent.conf".format(agent_name), "w+") as f:
312+ with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf'), "w+") as f:
313 for key in tf_conf:
314 if key == "job_queues":
315 f.write(key+":\n"+"- "+machine["cid"] + "\n")
316 else:
317 f.write(key + ": " + tf_conf[key] + "\n")
318
319-def create_agent_yaml(db_machine_list):
320+def create_agent_yaml(db_machine_list, cfg_path):
321
322- if not os.path.isfile("ce-tf-agents.yaml"):
323+ # TODO: We need to separate configs for labs
324+ if not os.path.isfile(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml")):
325
326- with open("ce-tf-agents.yaml","w") as f:
327+ with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"w") as f:
328 data = {
329 "series": "focal",
330 "description": "cert-testflinger-agents",
331 "applications":{
332- "agent-host-server":{
333+ "agent-host-server" + "-tel-l5":{
334 "series": "focal",
335- "constraints": "tags=agent-host",
336+ "constraints": "tags=agent-host" + "-tel-l5",
337 "charm": "../charms/testflinger-agent-host-charm",
338 "num_units": 1,
339 "resources":{
340@@ -149,19 +157,21 @@ def create_agent_yaml(db_machine_list):
341 }
342 yaml.dump(data, f, default_flow_style=False, encoding='utf-8', allow_unicode=True, sort_keys=False)
343
344- with open("ce-tf-agents.yaml","r+") as f:
345+ with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"r+") as f:
346
347 data = yaml.safe_load(f)
348 for machine in db_machine_list:
349- agent_name = "sru" + machine["cid"].replace("-","")
350+ if machine['lab'] == '':
351+ continue
352+ agent_name = "dut" + machine["cid"].replace("-","")
353 if agent_name in data["applications"]:
354 print("exist")
355 else:
356 data["applications"][agent_name] = {
357 "charm":"../charms/testflinger-agent-charm",
358 "resources":{
359- "device_configfile":"data/{}/default.yaml".format(agent_name),
360- "testflinger_agent_configfile":"data/{}/testflinger-agent.conf".format(agent_name)
361+ "device_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'),
362+ "testflinger_agent_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf')
363 },
364 "to": ["agent-host-server"]
365 }
366@@ -183,10 +193,10 @@ def main():
367 db = get_db_obj(args.database)
368 cursor = get_cursor_obj(db)
369 machine_list = read_data_from_db(cursor)
370- create_agent_config_dir(machine_list)
371- generate_agent_config(machine_list)
372- generate_tf_config(machine_list)
373- create_agent_yaml(machine_list)
374+ create_agent_config_dir(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
375+ generate_agent_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
376+ generate_tf_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
377+ create_agent_yaml(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
378 close_db(db)
379
380 if __name__ == '__main__':
381diff --git a/build/lib/cc_lab_manager/gsheet_db/__init__.py b/build/lib/cc_lab_manager/gsheet_db/__init__.py
382new file mode 100644
383index 0000000..e69de29
384--- /dev/null
385+++ b/build/lib/cc_lab_manager/gsheet_db/__init__.py
386diff --git a/cc-lab-manager/gsheet-db/db-gsheet.py b/build/lib/cc_lab_manager/gsheet_db/db_gsheet.py
387similarity index 92%
388rename from cc-lab-manager/gsheet-db/db-gsheet.py
389rename to build/lib/cc_lab_manager/gsheet_db/db_gsheet.py
390index a0c7100..c535445 100644
391--- a/cc-lab-manager/gsheet-db/db-gsheet.py
392+++ b/build/lib/cc_lab_manager/gsheet_db/db_gsheet.py
393@@ -35,9 +35,9 @@ def iter_all_strings():
394 if tmp_string == "az":
395 return col_position_list
396
397-def get_column_to_be_updated():
398+def get_column_to_be_updated(token_file):
399
400- gc = pygsheets.authorize(service_file='token.json')
401+ gc = pygsheets.authorize(service_file=token_file)
402 sht = gc.open_by_url(gsheet_url)
403 wks = sht[0]
404 machine_list = wks.get_all_records(empty_value='', head=1, majdim='ROWS', numericise_data=False)
405@@ -61,7 +61,7 @@ def sync_c3_data_to_sheet(db_machine_list,token_file):
406 wks = sht[0]
407
408 # mapping table for position of column
409- col_to_be_updated = get_column_to_be_updated()
410+ col_to_be_updated = get_column_to_be_updated(token_file)
411
412 row_num = 2
413 cell_list = [] # list for storing cell that are waiting for update.
414@@ -73,9 +73,9 @@ def sync_c3_data_to_sheet(db_machine_list,token_file):
415 wks.update_cells(cell_list)
416
417
418-def read_data_from_db(cursor):
419+def read_data_from_db(cursor, token_file):
420
421- col_in_c3 = list(get_column_to_be_updated().keys())
422+ col_in_c3 = list(get_column_to_be_updated(token_file).keys())
423 sqlcmd = 'select {} from lab_hw'.format(",".join(col_in_c3[0:]))
424 cursor.execute(sqlcmd)
425 results = cursor.fetchall()
426@@ -99,9 +99,9 @@ def main():
427 gsheet_url = args.url
428 db = get_db_obj(args.database)
429 cursor = get_cursor_obj(db)
430- sync_c3_data_to_sheet(read_data_from_db(cursor),args.token)
431+ sync_c3_data_to_sheet(read_data_from_db(cursor, args.token),args.token)
432 close_db(db)
433
434
435 if __name__ == '__main__':
436- main()
437\ No newline at end of file
438+ main()
439diff --git a/cc-lab-manager/gsheet-db/gsheet-db.py b/build/lib/cc_lab_manager/gsheet_db/gsheet_db.py
440similarity index 100%
441rename from cc-lab-manager/gsheet-db/gsheet-db.py
442rename to build/lib/cc_lab_manager/gsheet_db/gsheet_db.py
443diff --git a/build/lib/cc_lab_manager/maas/__init__.py b/build/lib/cc_lab_manager/maas/__init__.py
444new file mode 100644
445index 0000000..e69de29
446--- /dev/null
447+++ b/build/lib/cc_lab_manager/maas/__init__.py
448diff --git a/cc-lab-manager/maas/create-maas-node.py b/build/lib/cc_lab_manager/maas/create_maas_node.py
449similarity index 88%
450rename from cc-lab-manager/maas/create-maas-node.py
451rename to build/lib/cc_lab_manager/maas/create_maas_node.py
452index e57d8a5..05be6dc 100644
453--- a/cc-lab-manager/maas/create-maas-node.py
454+++ b/build/lib/cc_lab_manager/maas/create_maas_node.py
455@@ -33,12 +33,11 @@ def read_data_from_db(cursor):
456
457 return list(results)
458
459-def get_maas_session(maas_ip):
460+def get_maas_session(maas_ip, maas_admin, maas_admin_password):
461 client = login(
462 "http://{}:5240/MAAS/".format(maas_ip),
463- username="ubuntu", password="ubuntu",
464+ username=maas_admin, password=maas_admin_password,
465 )
466- print(type(client))
467 return client
468
469 def get_mac_list(machine_list):
470@@ -63,7 +62,7 @@ def get_maas_mac_list(client):
471
472 return mac_list
473
474-def create_maas_node(machine_list):
475+def create_maas_node(machine_list, maas_admin, maas_admin_password):
476
477 mass_is_connecting = ""
478 client = ""
479@@ -72,28 +71,28 @@ def create_maas_node(machine_list):
480
481 if not mass_is_connecting:
482 mass_is_connecting = machine["MAAS_Server"]
483- client = get_maas_session(mass_is_connecting)
484+ client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
485 maas_mac_list = get_maas_mac_list(client)
486 elif machine["MAAS_Server"] != mass_is_connecting:
487 mass_is_connecting = machine["MAAS_Server"]
488- client = get_maas_session(mass_is_connecting)
489+ client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
490 maas_mac_list = get_maas_mac_list(client)
491
492 power_type = machine["Power"].lower() if machine["Power"] != "" else "manual"
493 power_parameter = {}
494 node_name = (machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
495 machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]).lower()
496+ node_name = machine["cid"].lower()
497
498- if machine["MAC"] in maas_mac_list:
499+ if machine["MAC"] in maas_mac_list or machine["MAC"] == '':
500 continue
501 if power_type == "apc" or power_type == "raritan":
502 power_parameter["power_address"] = machine["PDU_IP"]
503 power_parameter["node_outlet"] = machine["PDU_Outlet"]
504 power_parameter["power_on_delay"] = 30
505-
506 elif power_type == "amt":
507 power_parameter["power_address"] = machine["IP"]
508- power_parameter["power_pass"] = "Insecure_101"
509+ power_parameter["power_pass"] = "Insecure-101"
510
511 maas_node = client.machines.create("amd64",machine["MAC"], "manual",hostname=node_name)
512
513@@ -111,11 +110,13 @@ def main():
514
515 parser = argparse.ArgumentParser()
516 parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
517+ parser.add_argument('--maas_admin', help="MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN'))
518+ parser.add_argument('--maas_admin_password', help="Password for MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN_PASSWD'))
519 args = parser.parse_args()
520 db = get_db_obj(args.database)
521 cursor = get_cursor_obj(db)
522 machine_list = read_data_from_db(cursor)
523- create_maas_node(machine_list)
524+ create_maas_node(machine_list, args.maas_admin, args.maas_admin_password)
525 update_node_id(cursor)
526 close_db(db)
527
528diff --git a/cc_lab_manager.egg-info/PKG-INFO b/cc_lab_manager.egg-info/PKG-INFO
529new file mode 100644
530index 0000000..03809d0
531--- /dev/null
532+++ b/cc_lab_manager.egg-info/PKG-INFO
533@@ -0,0 +1,11 @@
534+Metadata-Version: 1.1
535+Name: cc-lab-manager
536+Version: 0.1
537+Summary: Automated managing hardwares in Canonical Certification Labs
538+Home-page: https://code.launchpad.net/cc-lab-manager/
539+Author: Gavin Lin (gavin.lin)
540+Author-email: gavin.lin@canonical.com
541+License: UNKNOWN
542+Description: UNKNOWN
543+Platform: UNKNOWN
544+Classifier: Programming Language :: Python
545diff --git a/cc_lab_manager.egg-info/SOURCES.txt b/cc_lab_manager.egg-info/SOURCES.txt
546new file mode 100644
547index 0000000..7cfee1b
548--- /dev/null
549+++ b/cc_lab_manager.egg-info/SOURCES.txt
550@@ -0,0 +1,23 @@
551+README.md
552+setup.cfg
553+setup.py
554+cc_lab_manager/__init__.py
555+cc_lab_manager.egg-info/PKG-INFO
556+cc_lab_manager.egg-info/SOURCES.txt
557+cc_lab_manager.egg-info/dependency_links.txt
558+cc_lab_manager.egg-info/entry_points.txt
559+cc_lab_manager.egg-info/requires.txt
560+cc_lab_manager.egg-info/top_level.txt
561+cc_lab_manager/c3_db/__init__.py
562+cc_lab_manager/c3_db/c3_db.py
563+cc_lab_manager/commands/__init__.py
564+cc_lab_manager/commands/cc_lab_manager.py
565+cc_lab_manager/dhcp/__init__.py
566+cc_lab_manager/dhcp/dhcp_config_generator.py
567+cc_lab_manager/gen_config/__init__.py
568+cc_lab_manager/gen_config/gen_agent_tf_config.py
569+cc_lab_manager/gsheet_db/__init__.py
570+cc_lab_manager/gsheet_db/db_gsheet.py
571+cc_lab_manager/gsheet_db/gsheet_db.py
572+cc_lab_manager/maas/__init__.py
573+cc_lab_manager/maas/create_maas_node.py
574\ No newline at end of file
575diff --git a/cc_lab_manager.egg-info/dependency_links.txt b/cc_lab_manager.egg-info/dependency_links.txt
576new file mode 100644
577index 0000000..8b13789
578--- /dev/null
579+++ b/cc_lab_manager.egg-info/dependency_links.txt
580@@ -0,0 +1 @@
581+
582diff --git a/cc_lab_manager.egg-info/entry_points.txt b/cc_lab_manager.egg-info/entry_points.txt
583new file mode 100644
584index 0000000..21bd2e3
585--- /dev/null
586+++ b/cc_lab_manager.egg-info/entry_points.txt
587@@ -0,0 +1,3 @@
588+[console_scripts]
589+cc-lab-manager = cc_lab_manager.commands.cc_lab_manager:main
590+
591diff --git a/cc_lab_manager.egg-info/requires.txt b/cc_lab_manager.egg-info/requires.txt
592new file mode 100644
593index 0000000..26dccbc
594--- /dev/null
595+++ b/cc_lab_manager.egg-info/requires.txt
596@@ -0,0 +1,4 @@
597+aiohttp==3.7.2
598+jinja2
599+pysheets
600+python-libmaas
601diff --git a/cc_lab_manager.egg-info/top_level.txt b/cc_lab_manager.egg-info/top_level.txt
602new file mode 100644
603index 0000000..b831c50
604--- /dev/null
605+++ b/cc_lab_manager.egg-info/top_level.txt
606@@ -0,0 +1 @@
607+cc_lab_manager
608diff --git a/cc_lab_manager/__init__.py b/cc_lab_manager/__init__.py
609new file mode 100644
610index 0000000..e69de29
611--- /dev/null
612+++ b/cc_lab_manager/__init__.py
613diff --git a/cc_lab_manager/c3_db/__init__.py b/cc_lab_manager/c3_db/__init__.py
614new file mode 100644
615index 0000000..e69de29
616--- /dev/null
617+++ b/cc_lab_manager/c3_db/__init__.py
618diff --git a/cc_lab_manager/c3_db/c3_db.py b/cc_lab_manager/c3_db/c3_db.py
619new file mode 100644
620index 0000000..b9b0d7e
621--- /dev/null
622+++ b/cc_lab_manager/c3_db/c3_db.py
623@@ -0,0 +1,128 @@
624+import sqlite3
625+import logging
626+import argparse
627+import c3.api.query as c3query
628+from c3.api.api_utils import APIQuery
629+import os
630+
631+logger = logging.getLogger('cc-lab-manager')
632+
633+
634+def c3_to_db(db_path):
635+ """
636+ Query machine info from c3 and write to db.
637+ """
638+
639+ # Read CID from db
640+ try:
641+ hwdb = sqlite3.connect(db_path)
642+ cur = hwdb.execute('SELECT cid FROM lab_hw')
643+ cid_list = cur.fetchall()
644+ except Exception as e:
645+ logger.error('Error when reading db: ', e)
646+
647+ for cid_db in cid_list:
648+ cid = ''.join(cid_db)
649+ logger.info('Processing {}...'.format(cid))
650+ if '-' not in cid or 'TEL' in cid:
651+ continue
652+
653+ # Query hardware info from c3
654+ result = ''
655+ try:
656+ result = c3query.query_over_api_hardware(cid)
657+ except Exception as e:
658+ logger.error("Error when query c3: ", e)
659+
660+ if result:
661+ # Map hardware info from c3 to db column
662+ try:
663+ location = result['location']['name']
664+ except Exception as e:
665+ logger.warning('Failed Reading location: {}'.format(e))
666+ location = 'None'
667+ try:
668+ account = result['account']['name']
669+ except Exception as e:
670+ logger.warning('Failed reading account: {}'.format(e))
671+ account = 'None'
672+ try:
673+ platform_name = result['platform']['name']
674+ codename = result['platform']['codename']
675+ form_factor = result['platform']['form_factor']
676+ model = result['platform']['aliases']
677+ except Exception as e:
678+ logger.warning('Failed reading platform: {}'.format(e))
679+ platform_name = 'None'
680+ codename = 'None'
681+ form_factor = 'None'
682+ model = 'None'
683+ try:
684+ canonical_contact = result['canonical_contact']['display_name']
685+ except Exception as e:
686+ logger.warning('Failed reading canonical_contact: {}'
687+ .format(e))
688+ canonical_contact = 'None'
689+ try:
690+ holder = result['holder']['name']
691+ except Exception as e:
692+ logger.warning('Failed reading holder: {}'.format(e))
693+ holder = 'None'
694+ hw_info = {'Location': location,
695+ 'Status': result['status'],
696+ 'Account': account,
697+ 'Project': result['project_name'],
698+ 'Model': model,
699+ 'CanonicalLabel': result['canonical_label'],
700+ 'PlatformName': platform_name,
701+ 'CodeName': codename,
702+ 'SKU': result['sku'],
703+ 'HardwareBuild': result['hardware_build'],
704+ 'Form': form_factor,
705+ 'CanonicalContact': canonical_contact,
706+ 'Holder': holder,
707+ 'SecureID': result['secure_id']}
708+
709+ # Write hardware info to db
710+ try:
711+ for key in hw_info:
712+ sql_cmd = 'UPDATE lab_hw SET {} = "{}" WHERE CID = "{}"' \
713+ .format(key, hw_info[key],
714+ result['canonical_id'])
715+ hwdb.execute(sql_cmd)
716+ hwdb.commit()
717+ except Exception as e:
718+ logger.error('Error writing to db: ', e)
719+ hwdb.close()
720+
721+ hwdb.close()
722+
723+
724+def environ_or_required(key):
725+ if os.environ.get(key):
726+ return {'default': os.environ.get(key)}
727+ else:
728+ return {'required': True}
729+
730+
731+def main():
732+ parser = argparse.ArgumentParser()
733+ parser.add_argument('--conf', help="Config file for C3 API", default = os.environ.get('CCLM_CC_TOOL_BOX') + '/config/c3-cli/my_conf.ini')
734+ parser.add_argument('--c3user', help="C3 User Account", **environ_or_required('CCLM_C3_USERNAME'))
735+ parser.add_argument('--c3apikey', help="C3 API Key", **environ_or_required('CCLM_C3_APIKEY'))
736+ parser.add_argument('--hwdb', help="C3 API Key", **environ_or_required('CCLM_HARDWARE_DB'))
737+ args = parser.parse_args()
738+
739+ ci = c3query.configuration.get_instance()
740+ ci.read_configuration(args.conf)
741+
742+ api = APIQuery(ci.config['C3']['URI'])
743+ rparam = {"username": args.c3user,
744+ "api_key": args.c3apikey}
745+
746+ c3query.api_instance.set_api_params(api, rparam)
747+ c3_to_db(args.hwdb)
748+
749+
750+if __name__ == '__main__':
751+ main()
752diff --git a/cc_lab_manager/commands/__init__.py b/cc_lab_manager/commands/__init__.py
753new file mode 100644
754index 0000000..e69de29
755--- /dev/null
756+++ b/cc_lab_manager/commands/__init__.py
757diff --git a/cc_lab_manager/commands/cc_lab_manager.py b/cc_lab_manager/commands/cc_lab_manager.py
758new file mode 100644
759index 0000000..7da03df
760--- /dev/null
761+++ b/cc_lab_manager/commands/cc_lab_manager.py
762@@ -0,0 +1,49 @@
763+import logging
764+import os
765+import sys
766+
767+from cc_lab_manager.gsheet_db import gsheet_db
768+from cc_lab_manager.gsheet_db import db_gsheet
769+from cc_lab_manager.c3_db import c3_db
770+from cc_lab_manager.dhcp import dhcp_config_generator
771+from cc_lab_manager.maas import create_maas_node
772+from cc_lab_manager.gen_config import gen_agent_tf_config
773+
774+logger = logging.getLogger('cc-lab-manager')
775+
776+def main():
777+ # Read data from the google sheet to db
778+ gsheet_db.main()
779+
780+ # Get needed information from c3
781+ c3_db.main()
782+
783+ # Generate DHCP config for each subnets
784+ # TODO: Currently these config files need to be copied
785+ # manually, then MAAS will apply it automatically
786+ dhcp_config_generator.main()
787+
788+ # Create MAAS nodes for DUTs
789+ create_maas_node.main()
790+
791+ # Create agent configs for DUTs
792+ # TODO: Currently these configs need to be push/pull
793+ # and applied manually
794+ gen_agent_tf_config.main()
795+
796+ # Write data in db back to the google sheet
797+ db_gsheet.main()
798+
799+ #ci = c3query.configuration.get_instance()
800+
801+ #api = APIQuery(ci.config['C3']['URI'])
802+ #rparam = {"username": ci.config['C3']['UserName'],
803+ # "api_key": ci.config['C3']['APIKey']}
804+
805+ #c3query.api_instance.set_api_params(api, rparam)
806+ #hwdb = './hardware.db'
807+ #c3_to_db(hwdb)
808+
809+
810+if __name__ == '__main__':
811+ main()
812diff --git a/cc_lab_manager/dhcp/__init__.py b/cc_lab_manager/dhcp/__init__.py
813new file mode 100644
814index 0000000..e69de29
815--- /dev/null
816+++ b/cc_lab_manager/dhcp/__init__.py
817diff --git a/cc_lab_manager/dhcp/dhcp_config_generator.py b/cc_lab_manager/dhcp/dhcp_config_generator.py
818new file mode 100644
819index 0000000..e586dd4
820--- /dev/null
821+++ b/cc_lab_manager/dhcp/dhcp_config_generator.py
822@@ -0,0 +1,96 @@
823+import sqlite3
824+import logging
825+import argparse
826+import os
827+from jinja2 import Template
828+
829+logger = logging.getLogger('cc-lab-manager')
830+
831+
832+def read_data_from_db(path_to_db):
833+ db = sqlite3.connect(path_to_db)
834+ db.row_factory = sqlite3.Row
835+ col_in_db = ["CID", "MAC", "Controller_MAC", "Provision", "IP",
836+ "Controller_IP", "Lab", "Frame", "Shelf", "Partition"]
837+ sqlcmd = ('select {},{},{},{},{},{},{},{},{},{} from lab_hw'
838+ .format(*col_in_db))
839+ db_row_objs = db.execute(sqlcmd).fetchall()
840+ db.close()
841+ db_dict_list = []
842+ for row_obj in db_row_objs:
843+ db_dict_list.append({k: row_obj[k] for k in row_obj.keys()})
844+ return db_dict_list
845+
846+
847+class DhcpManager:
848+ def __init__(self):
849+ self.dhcp_template = '''
850+# {{CID}}
851+host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}} {
852+ hardware ethernet {{MAC}};
853+ fixed-address {{IP}};
854+}
855+
856+# {{CID}}-controller
857+host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}}-ctl {
858+ hardware ethernet {{Controller_MAC}};
859+ fixed-address {{Controller_IP}};
860+}
861+
862+'''
863+
864+ def generate_dhcp_conf(self, ip_mac_list, dhcp_conf_path):
865+ dhcp_conf_data = {}
866+ for ip_mac in ip_mac_list:
867+ if ip_mac['IP'] == '' or ip_mac['Controller_IP'] == ''\
868+ or ip_mac['Lab'] == '' or ip_mac['Frame'] == ''\
869+ or ip_mac['Shelf'] == '' or ip_mac['Partition'] == '':
870+ if ip_mac['CID'] == '':
871+ ip_mac_list.remove(ip_mac)
872+ else:
873+ logger.info("Missing required information for", ip_mac['CID'])
874+ continue
875+
876+ # Generate MAC for dummy devices or devices without fixed MAC
877+ if ip_mac['MAC'] == '':
878+ ip = ip_mac['IP'].split('.')
879+ ip_mac['MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
880+ f'{int(ip[2]):02x}:{int(ip[3]):02x}')
881+ if ip_mac['Controller_MAC'] == '':
882+ ip = ip_mac['Controller_IP'].split('.')
883+ ip_mac['Controller_MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
884+ f'{int(ip[2]):02x}:'
885+ f'{int(ip[3]):02x}')
886+
887+ tm = Template(self.dhcp_template)
888+ machine_ip = tm.render(ip_mac)
889+ if ip_mac['Lab'] not in dhcp_conf_data:
890+ dhcp_conf_data[ip_mac['Lab']] = machine_ip
891+ else:
892+ dhcp_conf_data[ip_mac['Lab']] += machine_ip
893+
894+ for lab_id in dhcp_conf_data:
895+ with open(os.path.join(dhcp_conf_path, 'cert-' + lab_id.lower() + '-dhcpd-pool.conf'), 'w') as out:
896+ out.write(dhcp_conf_data[lab_id])
897+
898+
899+def environ_or_required(key):
900+ if os.environ.get(key):
901+ return {'default': os.environ.get(key)}
902+ else:
903+ return {'required': True}
904+
905+
906+def main():
907+ parser = argparse.ArgumentParser()
908+ parser.add_argument('--cctoolbox', help="Path to cc-tool-box for storing dhcp config", **environ_or_required('CCLM_CC_TOOL_BOX'))
909+ parser.add_argument('--hwdb', help="Hardware db to read ip mapping", **environ_or_required('CCLM_HARDWARE_DB'))
910+ args = parser.parse_args()
911+
912+ ip_mac_list = read_data_from_db(args.hwdb)
913+ dm = DhcpManager()
914+ dm.generate_dhcp_conf(ip_mac_list, os.path.join(args.cctoolbox, 'config', 'dhcp'))
915+
916+
917+if __name__ == '__main__':
918+ main()
919diff --git a/cc_lab_manager/gen_config/__init__.py b/cc_lab_manager/gen_config/__init__.py
920new file mode 100644
921index 0000000..e69de29
922--- /dev/null
923+++ b/cc_lab_manager/gen_config/__init__.py
924diff --git a/cc_lab_manager/gen_config/gen_agent_tf_config.py b/cc_lab_manager/gen_config/gen_agent_tf_config.py
925new file mode 100644
926index 0000000..8056656
927--- /dev/null
928+++ b/cc_lab_manager/gen_config/gen_agent_tf_config.py
929@@ -0,0 +1,203 @@
930+import sqlite3
931+import os
932+import yaml
933+import argparse
934+
935+agent_config = {"device_ip": None,
936+ "secure_id": None,
937+ "node_id": None,
938+ "maas_user": "cert_maas_admin",
939+ "agent_name": None,
940+ "node_name": None,
941+ "reset_efi": "true",
942+ "env":{"HEXR_DEVICE_SECURE_ID": None,
943+ "WPA_BG_SSID": "ubuntu-cert-bg-wpa-tpelab",
944+ "WPA_BG_PSK": "insecure",
945+ "WPA_N_SSID": "ubuntu-cert-n-wpa-tpelab",
946+ "WPA_N_PSK": "insecure",
947+ "WPA_AC_SSID": "ubuntu-cert-ac-wpa-tpelab",
948+ "WPA_AC_PSK": "insecure",
949+ "OPEN_BG_SSID": "ubuntu-cert-bg-open-tpelab",
950+ "OPEN_N_SSID": "ubuntu-cert-n-open-tpelab",
951+ "OPEN_AC_SSID": "ubuntu-cert-ac-open-tpelab",
952+ "DEVICE_IP": None
953+ }
954+ }
955+
956+tf_config = {"agent_id": None,
957+ "server_address": "https://testflinger.canonical.com",
958+ "global_timeout": "43200",
959+ "output_timeout": "8000",
960+ "execution_basedir": "/home/ubuntu/testflinger/{}/run",
961+ "logging_basedir": "/home/ubuntu/testflinger/{}/logs",
962+ "results_basedir": "/home/ubuntu/testflinger/{}/results",
963+ "logging_level": "INFO",
964+ "job_queues": None,
965+ "setup_command": "tf-setup",
966+ "provision_command": "tf-provision",
967+ "test_command": "tf-test",
968+ "reserve_command": "tf-reserve",
969+ "cleanup_command": "tf-cleanup",
970+ "provision_type": "maas2"
971+ }
972+
973+def get_db_obj(path_to_db):
974+
975+ db = sqlite3.connect(path_to_db)
976+ db.row_factory = sqlite3.Row
977+ return db
978+
979+def get_cursor_obj(db_obj):
980+
981+ cursor = db_obj.cursor()
982+ return cursor
983+
984+def close_db(db):
985+ db.commit()
986+ db.close()
987+
988+def read_data_from_db(cursor):
989+
990+ col_in_c3 = ["CID","Lab","IP","SKU","MAAS_Node_ID","Model","SecureID"]
991+
992+ sqlcmd = 'select {},{},{},{},{},{},{} from lab_hw where CID not like \'TEL-%\''.format(*col_in_c3)
993+ cursor.execute(sqlcmd)
994+ results = cursor.fetchall()
995+ return list(results)
996+
997+def create_agent_config_dir(db_machine_list, cfg_path):
998+
999+ for machine in db_machine_list:
1000+
1001+ if machine['lab'] == '':
1002+ continue
1003+ agent_name = "dut"+machine["cid"].replace("-","")
1004+ try:
1005+ os.makedirs(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name))
1006+ except:
1007+ print("dir exist")
1008+
1009+def generate_agent_config(db_machine_list, cfg_path):
1010+
1011+ for machine in db_machine_list:
1012+
1013+ if machine['lab'] == '':
1014+ continue
1015+ agent_conf = agent_config.copy()
1016+ agent_name = "dut" + machine["cid"].replace("-","")
1017+ secure_id = machine["SecureID"]
1018+ ip = machine["IP"]
1019+ node_id = machine["MAAS_Node_ID"]
1020+ # node_name = machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
1021+ # machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]
1022+ node_name = machine["cid"]
1023+
1024+ agent_conf["node_name"] = node_name.lower()
1025+ agent_conf["agent_name"] = agent_name
1026+ agent_conf["secure_id"] = secure_id
1027+ agent_conf["node_id"] = node_id
1028+ agent_conf["device_ip"] = ip
1029+ agent_conf["env"]["HEXR_DEVICE_SECURE_ID"] = secure_id
1030+ agent_conf["env"]["DEVICE_IP"] = ip
1031+
1032+ with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'), "w+") as f:
1033+
1034+ for key in agent_config:
1035+ if key == "env":
1036+ f.write(key+" :\n")
1037+ for env_key in agent_config["env"]:
1038+ if "SSID" in env_key:
1039+ agent_conf["env"][env_key] += machine["lab"][-1]
1040+ f.write(" {key}: {value}\n".format(key=env_key,value=agent_conf["env"][env_key]))
1041+ else:
1042+ f.write("{key}: {value}\n".format(key=key,value=agent_conf[key]))
1043+
1044+
1045+def generate_tf_config(db_machine_list, cfg_path):
1046+
1047+ for machine in db_machine_list:
1048+ if machine['lab'] == '':
1049+ continue
1050+ tf_conf = tf_config.copy()
1051+
1052+ agent_name = "dut" + machine["cid"].replace("-","")
1053+ tf_conf["agent_id"] = agent_name
1054+ tf_conf["execution_basedir"] = tf_conf["execution_basedir"].format(agent_name)
1055+ tf_conf["logging_basedir"] = tf_conf["logging_basedir"].format(agent_name)
1056+ tf_conf["results_basedir"] = tf_conf["results_basedir"].format(agent_name)
1057+
1058+ with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf'), "w+") as f:
1059+ for key in tf_conf:
1060+ if key == "job_queues":
1061+ f.write(key+":\n"+"- "+machine["cid"] + "\n")
1062+ else:
1063+ f.write(key + ": " + tf_conf[key] + "\n")
1064+
1065+def create_agent_yaml(db_machine_list, cfg_path):
1066+
1067+ # TODO: We need to separate configs for labs
1068+ if not os.path.isfile(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml")):
1069+
1070+ with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"w") as f:
1071+ data = {
1072+ "series": "focal",
1073+ "description": "cert-testflinger-agents",
1074+ "applications":{
1075+ "agent-host-server" + "-tel-l5":{
1076+ "series": "focal",
1077+ "constraints": "tags=agent-host" + "-tel-l5",
1078+ "charm": "../charms/testflinger-agent-host-charm",
1079+ "num_units": 1,
1080+ "resources":{
1081+ "ssh_priv_key": "agent-host/id_rsa",
1082+ "ssh_pub_key": "agent-host/id_rsa.pub",
1083+ },
1084+ }
1085+ }
1086+ }
1087+ yaml.dump(data, f, default_flow_style=False, encoding='utf-8', allow_unicode=True, sort_keys=False)
1088+
1089+ with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"r+") as f:
1090+
1091+ data = yaml.safe_load(f)
1092+ for machine in db_machine_list:
1093+ if machine['lab'] == '':
1094+ continue
1095+ agent_name = "dut" + machine["cid"].replace("-","")
1096+ if agent_name in data["applications"]:
1097+ print("exist")
1098+ else:
1099+ data["applications"][agent_name] = {
1100+ "charm":"../charms/testflinger-agent-charm",
1101+ "resources":{
1102+ "device_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'),
1103+ "testflinger_agent_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf')
1104+ },
1105+ "to": ["agent-host-server"]
1106+ }
1107+ f.seek(0)
1108+ yaml.dump(data, f, default_flow_style=False, encoding='utf-8', allow_unicode=True, sort_keys=False)
1109+
1110+
1111+def environ_or_required(key):
1112+ if os.environ.get(key):
1113+ return {'default': os.environ.get(key)}
1114+ else:
1115+ return {'required': True}
1116+
1117+def main():
1118+
1119+ parser = argparse.ArgumentParser()
1120+ parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
1121+ args = parser.parse_args()
1122+ db = get_db_obj(args.database)
1123+ cursor = get_cursor_obj(db)
1124+ machine_list = read_data_from_db(cursor)
1125+ create_agent_config_dir(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
1126+ generate_agent_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
1127+ generate_tf_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
1128+ create_agent_yaml(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
1129+ close_db(db)
1130+
1131+if __name__ == '__main__':
1132+ main()
1133diff --git a/cc_lab_manager/gsheet_db/__init__.py b/cc_lab_manager/gsheet_db/__init__.py
1134new file mode 100644
1135index 0000000..e69de29
1136--- /dev/null
1137+++ b/cc_lab_manager/gsheet_db/__init__.py
1138diff --git a/cc_lab_manager/gsheet_db/db_gsheet.py b/cc_lab_manager/gsheet_db/db_gsheet.py
1139new file mode 100644
1140index 0000000..c535445
1141--- /dev/null
1142+++ b/cc_lab_manager/gsheet_db/db_gsheet.py
1143@@ -0,0 +1,107 @@
1144+import pygsheets
1145+import sqlite3
1146+import sys
1147+from string import ascii_lowercase
1148+import itertools
1149+import argparse
1150+import os
1151+
1152+gsheet_url = ''
1153+start_column_name = 'Location'
1154+
1155+def get_db_obj(path_to_db):
1156+
1157+ db = sqlite3.connect(path_to_db)
1158+ db.row_factory = sqlite3.Row
1159+ return db
1160+
1161+def get_cursor_obj(db_obj):
1162+
1163+ cursor = db_obj.cursor()
1164+ return cursor
1165+
1166+def close_db(db):
1167+ db.commit()
1168+ db.close()
1169+
1170+def iter_all_strings():
1171+ # generate alphabet list a,b,c.........az for accessing cell's position.
1172+
1173+ col_position_list = []
1174+ for size in itertools.count(1):
1175+ for s in itertools.product(ascii_lowercase,repeat = size):
1176+ tmp_string = "".join(s)
1177+ col_position_list.append("".join(tmp_string))
1178+ if tmp_string == "az":
1179+ return col_position_list
1180+
1181+def get_column_to_be_updated(token_file):
1182+
1183+ gc = pygsheets.authorize(service_file=token_file)
1184+ sht = gc.open_by_url(gsheet_url)
1185+ wks = sht[0]
1186+ machine_list = wks.get_all_records(empty_value='', head=1, majdim='ROWS', numericise_data=False)
1187+ keylist = list(machine_list[0].keys())
1188+
1189+ started_index = keylist.index(start_column_name)
1190+ col_to_be_updated = dict.fromkeys(keylist[started_index:])
1191+ col_position_list = iter_all_strings()
1192+
1193+ for key in col_to_be_updated:
1194+ col_to_be_updated[key] = col_position_list[started_index]
1195+ started_index += 1
1196+
1197+ return col_to_be_updated
1198+
1199+
1200+def sync_c3_data_to_sheet(db_machine_list,token_file):
1201+
1202+ gc = pygsheets.authorize(service_file=token_file)
1203+ sht = gc.open_by_url(gsheet_url)
1204+ wks = sht[0]
1205+
1206+ # mapping table for position of column
1207+ col_to_be_updated = get_column_to_be_updated(token_file)
1208+
1209+ row_num = 2
1210+ cell_list = [] # list for storing cell that are waiting for update.
1211+ for machine in db_machine_list:
1212+ for key in col_to_be_updated:
1213+ cell_list.append(pygsheets.Cell(col_to_be_updated[key]+str(row_num),machine[key]))
1214+
1215+ row_num += 1
1216+ wks.update_cells(cell_list)
1217+
1218+
1219+def read_data_from_db(cursor, token_file):
1220+
1221+ col_in_c3 = list(get_column_to_be_updated(token_file).keys())
1222+ sqlcmd = 'select {} from lab_hw'.format(",".join(col_in_c3[0:]))
1223+ cursor.execute(sqlcmd)
1224+ results = cursor.fetchall()
1225+
1226+ return list(results)
1227+
1228+def environ_or_required(key):
1229+ if os.environ.get(key):
1230+ return {'default': os.environ.get(key)}
1231+ else:
1232+ return {'required': True}
1233+
1234+def main():
1235+
1236+ parser = argparse.ArgumentParser()
1237+ parser.add_argument('--url', help="Google sheets URL",**environ_or_required('CCLM_GOOGLE_SHEET'))
1238+ parser.add_argument('--token', help="Google API token",**environ_or_required('CCLM_GOOGLE_TOKEN'))
1239+ parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
1240+ args = parser.parse_args()
1241+ global gsheet_url
1242+ gsheet_url = args.url
1243+ db = get_db_obj(args.database)
1244+ cursor = get_cursor_obj(db)
1245+ sync_c3_data_to_sheet(read_data_from_db(cursor, args.token),args.token)
1246+ close_db(db)
1247+
1248+
1249+if __name__ == '__main__':
1250+ main()
1251diff --git a/cc_lab_manager/gsheet_db/gsheet_db.py b/cc_lab_manager/gsheet_db/gsheet_db.py
1252new file mode 100644
1253index 0000000..1b11014
1254--- /dev/null
1255+++ b/cc_lab_manager/gsheet_db/gsheet_db.py
1256@@ -0,0 +1,91 @@
1257+import pygsheets
1258+import sqlite3
1259+import sys
1260+import argparse
1261+import os
1262+
1263+gsheet_url = ''
1264+
1265+def get_machine_list(token_file):
1266+
1267+ gc = pygsheets.authorize(service_file=token_file)
1268+ sht = gc.open_by_url(gsheet_url)
1269+ wks = sht[0]
1270+ machine_list = wks.get_all_records(empty_value='', head=1, majdim='ROWS', numericise_data=False)
1271+
1272+ return machine_list
1273+
1274+def get_db_obj(path_to_db):
1275+
1276+ db = sqlite3.connect(path_to_db)
1277+ return db
1278+
1279+def get_cursor_obj(db_obj):
1280+
1281+ cursor = db_obj.cursor()
1282+ return cursor
1283+
1284+def close_db(db):
1285+ db.commit()
1286+ db.close()
1287+
1288+def check_db_table_exist(cursor,table_name):
1289+
1290+ sqlcmd = 'select name from sqlite_master where type=\'table\' and name="{table_name}"'.format(table_name=table_name)
1291+ cursor.execute(sqlcmd)
1292+ result = cursor.fetchone()
1293+ return result
1294+
1295+def write_machine_list_to_db(cursor,token_file):
1296+
1297+ machine_list = get_machine_list(token_file)
1298+ keylist = [*machine_list[0]]
1299+ create_cmd = "CREATE TABLE lab_hw ({})".format(keylist[0]+ " PRIMARY KEY," +",".join(keylist[1:]))
1300+
1301+ #check table is exist or not
1302+ if check_db_table_exist(cursor,"lab_hw"):
1303+ print('table exist')
1304+ cursor.execute("DELETE FROM lab_hw")
1305+
1306+ else:
1307+ try:
1308+ cursor.execute(create_cmd)
1309+
1310+ except sqlite3.Error as er:
1311+ print('SQLite error: %s' % (' '.join(er.args)))
1312+
1313+ insert_cmd_fmt = 'insert into lab_hw values("{}")'
1314+
1315+ for machine in machine_list:
1316+ if machine["CID"] == "":
1317+ print("1")
1318+ machine["CID"] = machine["Lab"] + machine["Frame"] + machine["Shelf"] + machine["Partition"]
1319+ insert_cmd = insert_cmd_fmt.format('","'.join(machine.values()))
1320+ try:
1321+ cursor.execute(insert_cmd)
1322+
1323+ except sqlite3.Error as er:
1324+ print('SQLite error: %s' % (' '.join(er.args)))
1325+
1326+def environ_or_required(key):
1327+ if os.environ.get(key):
1328+ return {'default': os.environ.get(key)}
1329+ else:
1330+ return {'required': True}
1331+
1332+def main():
1333+
1334+ parser = argparse.ArgumentParser()
1335+ parser.add_argument('--url', help="Google sheets URL",**environ_or_required('CCLM_GOOGLE_SHEET'))
1336+ parser.add_argument('--token', help="Google API token",**environ_or_required('CCLM_GOOGLE_TOKEN'))
1337+ parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
1338+ args = parser.parse_args()
1339+ global gsheet_url
1340+ gsheet_url = args.url
1341+ db = get_db_obj(args.database)
1342+ cursor = get_cursor_obj(db)
1343+ write_machine_list_to_db(cursor,args.token)
1344+ close_db(db)
1345+
1346+if __name__ == '__main__':
1347+ main()
1348diff --git a/cc_lab_manager/maas/__init__.py b/cc_lab_manager/maas/__init__.py
1349new file mode 100644
1350index 0000000..e69de29
1351--- /dev/null
1352+++ b/cc_lab_manager/maas/__init__.py
1353diff --git a/cc_lab_manager/maas/create_maas_node.py b/cc_lab_manager/maas/create_maas_node.py
1354new file mode 100644
1355index 0000000..05be6dc
1356--- /dev/null
1357+++ b/cc_lab_manager/maas/create_maas_node.py
1358@@ -0,0 +1,124 @@
1359+from doctest import master
1360+from maas.client import connect
1361+from maas.client import login
1362+from maas.client.enum import LinkMode
1363+import argparse
1364+import os
1365+import sqlite3
1366+
1367+update_node_id_list = []
1368+
1369+def get_db_obj(path_to_db):
1370+
1371+ db = sqlite3.connect(path_to_db)
1372+ db.row_factory = sqlite3.Row
1373+ return db
1374+
1375+def get_cursor_obj(db_obj):
1376+
1377+ cursor = db_obj.cursor()
1378+ return cursor
1379+
1380+def close_db(db):
1381+ db.commit()
1382+ db.close()
1383+
1384+def read_data_from_db(cursor):
1385+
1386+ col_in_c3 = ["CID","MAC","Model","SKU","Power","IP","PDU_IP","PDU_Outlet","MAAS_Server","MAAS_Node_ID"]
1387+
1388+ sqlcmd = 'select {} from lab_hw where CID not like \'TEL-%\' order by MAAS_Server'.format(",".join(col_in_c3[0:]))
1389+ cursor.execute(sqlcmd)
1390+ results = cursor.fetchall()
1391+
1392+ return list(results)
1393+
1394+def get_maas_session(maas_ip, maas_admin, maas_admin_password):
1395+ client = login(
1396+ "http://{}:5240/MAAS/".format(maas_ip),
1397+ username=maas_admin, password=maas_admin_password,
1398+ )
1399+ return client
1400+
1401+def get_mac_list(machine_list):
1402+
1403+ mac_list = []
1404+ for machine in machine_list:
1405+ mac_list.append(machine["mac"])
1406+
1407+ return mac_list
1408+
1409+def update_node_id(cursor):
1410+
1411+ sqlcmd = 'update lab_hw set MAAS_Node_ID=? where CID=?'
1412+ cursor.executemany(sqlcmd, update_node_id_list)
1413+
1414+def get_maas_mac_list(client):
1415+
1416+ mac_list = []
1417+ for machine in client.machines.list():
1418+ for key in machine.interfaces.by_name:
1419+ mac_list.append(machine.interfaces.by_name[key].mac_address)
1420+
1421+ return mac_list
1422+
1423+def create_maas_node(machine_list, maas_admin, maas_admin_password):
1424+
1425+ mass_is_connecting = ""
1426+ client = ""
1427+
1428+ for machine in machine_list:
1429+
1430+ if not mass_is_connecting:
1431+ mass_is_connecting = machine["MAAS_Server"]
1432+ client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
1433+ maas_mac_list = get_maas_mac_list(client)
1434+ elif machine["MAAS_Server"] != mass_is_connecting:
1435+ mass_is_connecting = machine["MAAS_Server"]
1436+ client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
1437+ maas_mac_list = get_maas_mac_list(client)
1438+
1439+ power_type = machine["Power"].lower() if machine["Power"] != "" else "manual"
1440+ power_parameter = {}
1441+ node_name = (machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
1442+ machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]).lower()
1443+ node_name = machine["cid"].lower()
1444+
1445+ if machine["MAC"] in maas_mac_list or machine["MAC"] == '':
1446+ continue
1447+ if power_type == "apc" or power_type == "raritan":
1448+ power_parameter["power_address"] = machine["PDU_IP"]
1449+ power_parameter["node_outlet"] = machine["PDU_Outlet"]
1450+ power_parameter["power_on_delay"] = 30
1451+ elif power_type == "amt":
1452+ power_parameter["power_address"] = machine["IP"]
1453+ power_parameter["power_pass"] = "Insecure-101"
1454+
1455+ maas_node = client.machines.create("amd64",machine["MAC"], "manual",hostname=node_name)
1456+
1457+ update_node_id_list.append([maas_node.system_id, machine["CID"]])
1458+ maas_node.set_power(power_type=power_type.lower(),power_parameters=power_parameter)
1459+
1460+
1461+def environ_or_required(key):
1462+ if os.environ.get(key):
1463+ return {'default': os.environ.get(key)}
1464+ else:
1465+ return {'required': True}
1466+
1467+def main():
1468+
1469+ parser = argparse.ArgumentParser()
1470+ parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
1471+ parser.add_argument('--maas_admin', help="MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN'))
1472+ parser.add_argument('--maas_admin_password', help="Password for MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN_PASSWD'))
1473+ args = parser.parse_args()
1474+ db = get_db_obj(args.database)
1475+ cursor = get_cursor_obj(db)
1476+ machine_list = read_data_from_db(cursor)
1477+ create_maas_node(machine_list, args.maas_admin, args.maas_admin_password)
1478+ update_node_id(cursor)
1479+ close_db(db)
1480+
1481+if __name__ == '__main__':
1482+ main()
1483diff --git a/dist/cc_lab_manager-0.1-py3.8.egg b/dist/cc_lab_manager-0.1-py3.8.egg
1484new file mode 100644
1485index 0000000..cd9a0a4
1486Binary files /dev/null and b/dist/cc_lab_manager-0.1-py3.8.egg differ
1487diff --git a/setup.py b/setup.py
1488index a609291..d39888c 100644
1489--- a/setup.py
1490+++ b/setup.py
1491@@ -1,7 +1,7 @@
1492 #!/usr/bin/env python3
1493 from setuptools import setup, find_packages
1494
1495-INSTALL_REQUIRES = ['pysheets']
1496+INSTALL_REQUIRES = ['pysheets', 'jinja2', 'python-libmaas', 'aiohttp==3.7.2']
1497
1498 setup(
1499 name='cc-lab-manager',
1500@@ -12,4 +12,12 @@ setup(
1501 author_email='gavin.lin@canonical.com',
1502 url='https://code.launchpad.net/cc-lab-manager/',
1503 install_requires=INSTALL_REQUIRES,
1504+ entry_points={
1505+ 'console_scripts': [
1506+ 'cc-lab-manager=cc_lab_manager.commands.cc_lab_manager:main',
1507+ ]
1508+ },
1509+ classifiers=[
1510+ "Programming Language :: Python",
1511+ ]
1512 )

Subscribers

People subscribed via source and target branches

to all changes: