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
diff --git a/cc-lab-manager/c3-db/__init__.py b/build/lib/cc_lab_manager/__init__.py
0similarity index 100%0similarity index 100%
1rename from cc-lab-manager/c3-db/__init__.py1rename from cc-lab-manager/c3-db/__init__.py
2rename to build/lib/cc_lab_manager/__init__.py2rename to build/lib/cc_lab_manager/__init__.py
diff --git a/cc-lab-manager/dhcp/__init__.py b/build/lib/cc_lab_manager/c3_db/__init__.py
3similarity index 100%3similarity index 100%
4rename from cc-lab-manager/dhcp/__init__.py4rename from cc-lab-manager/dhcp/__init__.py
5rename to build/lib/cc_lab_manager/c3_db/__init__.py5rename to build/lib/cc_lab_manager/c3_db/__init__.py
diff --git a/cc-lab-manager/c3-db/c3-db.py b/build/lib/cc_lab_manager/c3_db/c3_db.py
6similarity index 86%6similarity index 86%
7rename from cc-lab-manager/c3-db/c3-db.py7rename from cc-lab-manager/c3-db/c3-db.py
8rename to build/lib/cc_lab_manager/c3_db/c3_db.py8rename to build/lib/cc_lab_manager/c3_db/c3_db.py
index a9f6779..b9b0d7e 100644
--- a/cc-lab-manager/c3-db/c3-db.py
+++ b/build/lib/cc_lab_manager/c3_db/c3_db.py
@@ -1,7 +1,9 @@
1import sqlite31import sqlite3
2import logging2import logging
3import argparse
3import c3.api.query as c3query4import c3.api.query as c3query
4from c3.api.api_utils import APIQuery5from c3.api.api_utils import APIQuery
6import os
57
6logger = logging.getLogger('cc-lab-manager')8logger = logging.getLogger('cc-lab-manager')
79
@@ -22,6 +24,8 @@ def c3_to_db(db_path):
22 for cid_db in cid_list:24 for cid_db in cid_list:
23 cid = ''.join(cid_db)25 cid = ''.join(cid_db)
24 logger.info('Processing {}...'.format(cid))26 logger.info('Processing {}...'.format(cid))
27 if '-' not in cid or 'TEL' in cid:
28 continue
2529
26 # Query hardware info from c330 # Query hardware info from c3
27 result = ''31 result = ''
@@ -94,17 +98,30 @@ def c3_to_db(db_path):
94 hwdb.close()98 hwdb.close()
9599
96100
101def environ_or_required(key):
102 if os.environ.get(key):
103 return {'default': os.environ.get(key)}
104 else:
105 return {'required': True}
106
107
97def main():108def main():
109 parser = argparse.ArgumentParser()
110 parser.add_argument('--conf', help="Config file for C3 API", default = os.environ.get('CCLM_CC_TOOL_BOX') + '/config/c3-cli/my_conf.ini')
111 parser.add_argument('--c3user', help="C3 User Account", **environ_or_required('CCLM_C3_USERNAME'))
112 parser.add_argument('--c3apikey', help="C3 API Key", **environ_or_required('CCLM_C3_APIKEY'))
113 parser.add_argument('--hwdb', help="C3 API Key", **environ_or_required('CCLM_HARDWARE_DB'))
114 args = parser.parse_args()
115
98 ci = c3query.configuration.get_instance()116 ci = c3query.configuration.get_instance()
99 ci.read_configuration('my_conf.ini')117 ci.read_configuration(args.conf)
100118
101 api = APIQuery(ci.config['C3']['URI'])119 api = APIQuery(ci.config['C3']['URI'])
102 rparam = {"username": ci.config['C3']['UserName'],120 rparam = {"username": args.c3user,
103 "api_key": ci.config['C3']['APIKey']}121 "api_key": args.c3apikey}
104122
105 c3query.api_instance.set_api_params(api, rparam)123 c3query.api_instance.set_api_params(api, rparam)
106 hwdb = './hardware.db'124 c3_to_db(args.hwdb)
107 c3_to_db(hwdb)
108125
109126
110if __name__ == '__main__':127if __name__ == '__main__':
diff --git a/build/lib/cc_lab_manager/commands/__init__.py b/build/lib/cc_lab_manager/commands/__init__.py
111new file mode 100644128new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/lib/cc_lab_manager/commands/__init__.py
diff --git a/build/lib/cc_lab_manager/commands/cc_lab_manager.py b/build/lib/cc_lab_manager/commands/cc_lab_manager.py
112new file mode 100644129new file mode 100644
index 0000000..7da03df
--- /dev/null
+++ b/build/lib/cc_lab_manager/commands/cc_lab_manager.py
@@ -0,0 +1,49 @@
1import logging
2import os
3import sys
4
5from cc_lab_manager.gsheet_db import gsheet_db
6from cc_lab_manager.gsheet_db import db_gsheet
7from cc_lab_manager.c3_db import c3_db
8from cc_lab_manager.dhcp import dhcp_config_generator
9from cc_lab_manager.maas import create_maas_node
10from cc_lab_manager.gen_config import gen_agent_tf_config
11
12logger = logging.getLogger('cc-lab-manager')
13
14def main():
15 # Read data from the google sheet to db
16 gsheet_db.main()
17
18 # Get needed information from c3
19 c3_db.main()
20
21 # Generate DHCP config for each subnets
22 # TODO: Currently these config files need to be copied
23 # manually, then MAAS will apply it automatically
24 dhcp_config_generator.main()
25
26 # Create MAAS nodes for DUTs
27 create_maas_node.main()
28
29 # Create agent configs for DUTs
30 # TODO: Currently these configs need to be push/pull
31 # and applied manually
32 gen_agent_tf_config.main()
33
34 # Write data in db back to the google sheet
35 db_gsheet.main()
36
37 #ci = c3query.configuration.get_instance()
38
39 #api = APIQuery(ci.config['C3']['URI'])
40 #rparam = {"username": ci.config['C3']['UserName'],
41 # "api_key": ci.config['C3']['APIKey']}
42
43 #c3query.api_instance.set_api_params(api, rparam)
44 #hwdb = './hardware.db'
45 #c3_to_db(hwdb)
46
47
48if __name__ == '__main__':
49 main()
diff --git a/build/lib/cc_lab_manager/dhcp/__init__.py b/build/lib/cc_lab_manager/dhcp/__init__.py
0new file mode 10064450new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/lib/cc_lab_manager/dhcp/__init__.py
diff --git a/cc-lab-manager/dhcp/dhcp-config-generator.py b/build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py
1similarity index 69%51similarity index 69%
2rename from cc-lab-manager/dhcp/dhcp-config-generator.py52rename from cc-lab-manager/dhcp/dhcp-config-generator.py
3rename to build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py53rename to build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py
index 4ea854f..e586dd4 100644
--- a/cc-lab-manager/dhcp/dhcp-config-generator.py
+++ b/build/lib/cc_lab_manager/dhcp/dhcp_config_generator.py
@@ -1,7 +1,8 @@
1import sqlite31import sqlite3
2import logging2import logging
3import argparse
4import os
3from jinja2 import Template5from jinja2 import Template
4import c3.api.query as c3query
56
6logger = logging.getLogger('cc-lab-manager')7logger = logging.getLogger('cc-lab-manager')
78
@@ -38,15 +39,19 @@ host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}}-ctl {
3839
39'''40'''
4041
41 def generate_dhcp_conf(self, ip_mac_list, dhcp_conf):42 def generate_dhcp_conf(self, ip_mac_list, dhcp_conf_path):
42 dhcp_conf_data = ''43 dhcp_conf_data = {}
43 for ip_mac in ip_mac_list:44 for ip_mac in ip_mac_list:
44 if ip_mac['IP'] == '' or ip_mac['Controller_IP'] == ''\45 if ip_mac['IP'] == '' or ip_mac['Controller_IP'] == ''\
45 or ip_mac['Lab'] == '' or ip_mac['Frame'] == ''\46 or ip_mac['Lab'] == '' or ip_mac['Frame'] == ''\
46 or ip_mac['Shelf'] == '' or ip_mac['Partition'] == '':47 or ip_mac['Shelf'] == '' or ip_mac['Partition'] == '':
47 logger.error("Missing required information for", ip_mac['CID'])48 if ip_mac['CID'] == '':
49 ip_mac_list.remove(ip_mac)
50 else:
51 logger.info("Missing required information for", ip_mac['CID'])
48 continue52 continue
4953
54 # Generate MAC for dummy devices or devices without fixed MAC
50 if ip_mac['MAC'] == '':55 if ip_mac['MAC'] == '':
51 ip = ip_mac['IP'].split('.')56 ip = ip_mac['IP'].split('.')
52 ip_mac['MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'57 ip_mac['MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
@@ -56,23 +61,35 @@ host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}}-ctl {
56 ip_mac['Controller_MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'61 ip_mac['Controller_MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
57 f'{int(ip[2]):02x}:'62 f'{int(ip[2]):02x}:'
58 f'{int(ip[3]):02x}')63 f'{int(ip[3]):02x}')
64
59 tm = Template(self.dhcp_template)65 tm = Template(self.dhcp_template)
60 machine_ip = tm.render(ip_mac)66 machine_ip = tm.render(ip_mac)
61 dhcp_conf_data += machine_ip67 if ip_mac['Lab'] not in dhcp_conf_data:
62 with open(dhcp_conf, 'w') as out:68 dhcp_conf_data[ip_mac['Lab']] = machine_ip
63 out.write(dhcp_conf_data)69 else:
70 dhcp_conf_data[ip_mac['Lab']] += machine_ip
6471
72 for lab_id in dhcp_conf_data:
73 with open(os.path.join(dhcp_conf_path, 'cert-' + lab_id.lower() + '-dhcpd-pool.conf'), 'w') as out:
74 out.write(dhcp_conf_data[lab_id])
6575
66def main():
6776
68 ci = c3query.configuration.get_instance()77def environ_or_required(key):
69 ci.read_configuration('my_conf.ini')78 if os.environ.get(key):
79 return {'default': os.environ.get(key)}
80 else:
81 return {'required': True}
82
83
84def main():
85 parser = argparse.ArgumentParser()
86 parser.add_argument('--cctoolbox', help="Path to cc-tool-box for storing dhcp config", **environ_or_required('CCLM_CC_TOOL_BOX'))
87 parser.add_argument('--hwdb', help="Hardware db to read ip mapping", **environ_or_required('CCLM_HARDWARE_DB'))
88 args = parser.parse_args()
7089
71 hwdb = ci.config['CC-LAB-MANAGER']['HWDB']90 ip_mac_list = read_data_from_db(args.hwdb)
72 dhcp_conf = ci.config['CC-LAB-MANAGER']['DHCP_CONFIG']
73 ip_mac_list = read_data_from_db(hwdb)
74 dm = DhcpManager()91 dm = DhcpManager()
75 dm.generate_dhcp_conf(ip_mac_list, dhcp_conf)92 dm.generate_dhcp_conf(ip_mac_list, os.path.join(args.cctoolbox, 'config', 'dhcp'))
7693
7794
78if __name__ == '__main__':95if __name__ == '__main__':
diff --git a/build/lib/cc_lab_manager/gen_config/__init__.py b/build/lib/cc_lab_manager/gen_config/__init__.py
79new file mode 10064496new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/lib/cc_lab_manager/gen_config/__init__.py
diff --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
80similarity index 73%97similarity index 73%
81rename from cc-lab-manager/gen-config/gen-agent-tf-config.py98rename from cc-lab-manager/gen-config/gen-agent-tf-config.py
82rename to build/lib/cc_lab_manager/gen_config/gen_agent_tf_config.py99rename to build/lib/cc_lab_manager/gen_config/gen_agent_tf_config.py
index 337fe3e..8056656 100644
--- a/cc-lab-manager/gen-config/gen-agent-tf-config.py
+++ b/build/lib/cc_lab_manager/gen_config/gen_agent_tf_config.py
@@ -6,7 +6,7 @@ import argparse
6agent_config = {"device_ip": None,6agent_config = {"device_ip": None,
7 "secure_id": None,7 "secure_id": None,
8 "node_id": None,8 "node_id": None,
9 "maas_user": "sru-pool",9 "maas_user": "cert_maas_admin",
10 "agent_name": None,10 "agent_name": None,
11 "node_name": None,11 "node_name": None,
12 "reset_efi": "true",12 "reset_efi": "true",
@@ -65,27 +65,32 @@ def read_data_from_db(cursor):
65 results = cursor.fetchall()65 results = cursor.fetchall()
66 return list(results)66 return list(results)
6767
68def create_agent_config_dir(db_machine_list):68def create_agent_config_dir(db_machine_list, cfg_path):
69 69
70 for machine in db_machine_list:70 for machine in db_machine_list:
71 71
72 agent_name = "sru"+machine["cid"].replace("-","")72 if machine['lab'] == '':
73 continue
74 agent_name = "dut"+machine["cid"].replace("-","")
73 try:75 try:
74 os.makedirs("{}".format(agent_name))76 os.makedirs(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name))
75 except:77 except:
76 print("dir exist")78 print("dir exist")
7779
78def generate_agent_config(db_machine_list):80def generate_agent_config(db_machine_list, cfg_path):
79 81
80 for machine in db_machine_list:82 for machine in db_machine_list:
81 83
84 if machine['lab'] == '':
85 continue
82 agent_conf = agent_config.copy()86 agent_conf = agent_config.copy()
83 agent_name = "sru" + machine["cid"].replace("-","")87 agent_name = "dut" + machine["cid"].replace("-","")
84 secure_id = machine["SecureID"]88 secure_id = machine["SecureID"]
85 ip = machine["IP"]89 ip = machine["IP"]
86 node_id = machine["MAAS_Node_ID"]90 node_id = machine["MAAS_Node_ID"]
87 node_name = machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \91 # node_name = machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
88 machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]92 # machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]
93 node_name = machine["cid"]
8994
90 agent_conf["node_name"] = node_name.lower()95 agent_conf["node_name"] = node_name.lower()
91 agent_conf["agent_name"] = agent_name96 agent_conf["agent_name"] = agent_name
@@ -95,7 +100,7 @@ def generate_agent_config(db_machine_list):
95 agent_conf["env"]["HEXR_DEVICE_SECURE_ID"] = secure_id100 agent_conf["env"]["HEXR_DEVICE_SECURE_ID"] = secure_id
96 agent_conf["env"]["DEVICE_IP"] = ip101 agent_conf["env"]["DEVICE_IP"] = ip
97 102
98 with open("{}/default.yaml".format(agent_name), "w+") as f:103 with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'), "w+") as f:
99 104
100 for key in agent_config:105 for key in agent_config:
101 if key == "env":106 if key == "env":
@@ -108,36 +113,39 @@ def generate_agent_config(db_machine_list):
108 f.write("{key}: {value}\n".format(key=key,value=agent_conf[key]))113 f.write("{key}: {value}\n".format(key=key,value=agent_conf[key]))
109114
110 115
111def generate_tf_config(db_machine_list):116def generate_tf_config(db_machine_list, cfg_path):
112 117
113 for machine in db_machine_list:118 for machine in db_machine_list:
119 if machine['lab'] == '':
120 continue
114 tf_conf = tf_config.copy()121 tf_conf = tf_config.copy()
115 122
116 agent_name = "sru" + machine["cid"].replace("-","")123 agent_name = "dut" + machine["cid"].replace("-","")
117 tf_conf["agent_id"] = agent_name124 tf_conf["agent_id"] = agent_name
118 tf_conf["execution_basedir"] = tf_conf["execution_basedir"].format(agent_name)125 tf_conf["execution_basedir"] = tf_conf["execution_basedir"].format(agent_name)
119 tf_conf["logging_basedir"] = tf_conf["logging_basedir"].format(agent_name)126 tf_conf["logging_basedir"] = tf_conf["logging_basedir"].format(agent_name)
120 tf_conf["results_basedir"] = tf_conf["results_basedir"].format(agent_name)127 tf_conf["results_basedir"] = tf_conf["results_basedir"].format(agent_name)
121 128
122 with open("{}/testflinger-agent.conf".format(agent_name), "w+") as f:129 with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf'), "w+") as f:
123 for key in tf_conf:130 for key in tf_conf:
124 if key == "job_queues":131 if key == "job_queues":
125 f.write(key+":\n"+"- "+machine["cid"] + "\n")132 f.write(key+":\n"+"- "+machine["cid"] + "\n")
126 else:133 else:
127 f.write(key + ": " + tf_conf[key] + "\n")134 f.write(key + ": " + tf_conf[key] + "\n")
128 135
129def create_agent_yaml(db_machine_list):136def create_agent_yaml(db_machine_list, cfg_path):
130 137
131 if not os.path.isfile("ce-tf-agents.yaml"):138 # TODO: We need to separate configs for labs
139 if not os.path.isfile(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml")):
132 140
133 with open("ce-tf-agents.yaml","w") as f:141 with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"w") as f:
134 data = {142 data = {
135 "series": "focal",143 "series": "focal",
136 "description": "cert-testflinger-agents",144 "description": "cert-testflinger-agents",
137 "applications":{145 "applications":{
138 "agent-host-server":{146 "agent-host-server" + "-tel-l5":{
139 "series": "focal",147 "series": "focal",
140 "constraints": "tags=agent-host",148 "constraints": "tags=agent-host" + "-tel-l5",
141 "charm": "../charms/testflinger-agent-host-charm",149 "charm": "../charms/testflinger-agent-host-charm",
142 "num_units": 1,150 "num_units": 1,
143 "resources":{151 "resources":{
@@ -149,19 +157,21 @@ def create_agent_yaml(db_machine_list):
149 }157 }
150 yaml.dump(data, f, default_flow_style=False, encoding='utf-8', allow_unicode=True, sort_keys=False)158 yaml.dump(data, f, default_flow_style=False, encoding='utf-8', allow_unicode=True, sort_keys=False)
151159
152 with open("ce-tf-agents.yaml","r+") as f:160 with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"r+") as f:
153 161
154 data = yaml.safe_load(f)162 data = yaml.safe_load(f)
155 for machine in db_machine_list:163 for machine in db_machine_list:
156 agent_name = "sru" + machine["cid"].replace("-","")164 if machine['lab'] == '':
165 continue
166 agent_name = "dut" + machine["cid"].replace("-","")
157 if agent_name in data["applications"]:167 if agent_name in data["applications"]:
158 print("exist")168 print("exist")
159 else:169 else:
160 data["applications"][agent_name] = {170 data["applications"][agent_name] = {
161 "charm":"../charms/testflinger-agent-charm",171 "charm":"../charms/testflinger-agent-charm",
162 "resources":{172 "resources":{
163 "device_configfile":"data/{}/default.yaml".format(agent_name),173 "device_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'),
164 "testflinger_agent_configfile":"data/{}/testflinger-agent.conf".format(agent_name)174 "testflinger_agent_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf')
165 },175 },
166 "to": ["agent-host-server"]176 "to": ["agent-host-server"]
167 }177 }
@@ -183,10 +193,10 @@ def main():
183 db = get_db_obj(args.database)193 db = get_db_obj(args.database)
184 cursor = get_cursor_obj(db)194 cursor = get_cursor_obj(db)
185 machine_list = read_data_from_db(cursor)195 machine_list = read_data_from_db(cursor)
186 create_agent_config_dir(machine_list)196 create_agent_config_dir(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
187 generate_agent_config(machine_list)197 generate_agent_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
188 generate_tf_config(machine_list)198 generate_tf_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
189 create_agent_yaml(machine_list)199 create_agent_yaml(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
190 close_db(db)200 close_db(db)
191201
192if __name__ == '__main__':202if __name__ == '__main__':
diff --git a/build/lib/cc_lab_manager/gsheet_db/__init__.py b/build/lib/cc_lab_manager/gsheet_db/__init__.py
193new file mode 100644203new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/lib/cc_lab_manager/gsheet_db/__init__.py
diff --git a/cc-lab-manager/gsheet-db/db-gsheet.py b/build/lib/cc_lab_manager/gsheet_db/db_gsheet.py
194similarity index 92%204similarity index 92%
195rename from cc-lab-manager/gsheet-db/db-gsheet.py205rename from cc-lab-manager/gsheet-db/db-gsheet.py
196rename to build/lib/cc_lab_manager/gsheet_db/db_gsheet.py206rename to build/lib/cc_lab_manager/gsheet_db/db_gsheet.py
index a0c7100..c535445 100644
--- a/cc-lab-manager/gsheet-db/db-gsheet.py
+++ b/build/lib/cc_lab_manager/gsheet_db/db_gsheet.py
@@ -35,9 +35,9 @@ def iter_all_strings():
35 if tmp_string == "az":35 if tmp_string == "az":
36 return col_position_list36 return col_position_list
3737
38def get_column_to_be_updated():38def get_column_to_be_updated(token_file):
39 39
40 gc = pygsheets.authorize(service_file='token.json')40 gc = pygsheets.authorize(service_file=token_file)
41 sht = gc.open_by_url(gsheet_url)41 sht = gc.open_by_url(gsheet_url)
42 wks = sht[0]42 wks = sht[0]
43 machine_list = wks.get_all_records(empty_value='', head=1, majdim='ROWS', numericise_data=False)43 machine_list = wks.get_all_records(empty_value='', head=1, majdim='ROWS', numericise_data=False)
@@ -61,7 +61,7 @@ def sync_c3_data_to_sheet(db_machine_list,token_file):
61 wks = sht[0]61 wks = sht[0]
62 62
63 # mapping table for position of column 63 # mapping table for position of column
64 col_to_be_updated = get_column_to_be_updated()64 col_to_be_updated = get_column_to_be_updated(token_file)
65 65
66 row_num = 266 row_num = 2
67 cell_list = [] # list for storing cell that are waiting for update.67 cell_list = [] # list for storing cell that are waiting for update.
@@ -73,9 +73,9 @@ def sync_c3_data_to_sheet(db_machine_list,token_file):
73 wks.update_cells(cell_list)73 wks.update_cells(cell_list)
74 74
7575
76def read_data_from_db(cursor):76def read_data_from_db(cursor, token_file):
77 77
78 col_in_c3 = list(get_column_to_be_updated().keys())78 col_in_c3 = list(get_column_to_be_updated(token_file).keys())
79 sqlcmd = 'select {} from lab_hw'.format(",".join(col_in_c3[0:]))79 sqlcmd = 'select {} from lab_hw'.format(",".join(col_in_c3[0:]))
80 cursor.execute(sqlcmd)80 cursor.execute(sqlcmd)
81 results = cursor.fetchall()81 results = cursor.fetchall()
@@ -99,9 +99,9 @@ def main():
99 gsheet_url = args.url99 gsheet_url = args.url
100 db = get_db_obj(args.database)100 db = get_db_obj(args.database)
101 cursor = get_cursor_obj(db) 101 cursor = get_cursor_obj(db)
102 sync_c3_data_to_sheet(read_data_from_db(cursor),args.token)102 sync_c3_data_to_sheet(read_data_from_db(cursor, args.token),args.token)
103 close_db(db)103 close_db(db)
104104
105105
106if __name__ == '__main__':106if __name__ == '__main__':
107 main()
108\ No newline at end of file107\ No newline at end of file
108 main()
diff --git a/cc-lab-manager/gsheet-db/gsheet-db.py b/build/lib/cc_lab_manager/gsheet_db/gsheet_db.py
109similarity index 100%109similarity index 100%
110rename from cc-lab-manager/gsheet-db/gsheet-db.py110rename from cc-lab-manager/gsheet-db/gsheet-db.py
111rename to build/lib/cc_lab_manager/gsheet_db/gsheet_db.py111rename to build/lib/cc_lab_manager/gsheet_db/gsheet_db.py
diff --git a/build/lib/cc_lab_manager/maas/__init__.py b/build/lib/cc_lab_manager/maas/__init__.py
112new file mode 100644112new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/lib/cc_lab_manager/maas/__init__.py
diff --git a/cc-lab-manager/maas/create-maas-node.py b/build/lib/cc_lab_manager/maas/create_maas_node.py
113similarity index 88%113similarity index 88%
114rename from cc-lab-manager/maas/create-maas-node.py114rename from cc-lab-manager/maas/create-maas-node.py
115rename to build/lib/cc_lab_manager/maas/create_maas_node.py115rename to build/lib/cc_lab_manager/maas/create_maas_node.py
index e57d8a5..05be6dc 100644
--- a/cc-lab-manager/maas/create-maas-node.py
+++ b/build/lib/cc_lab_manager/maas/create_maas_node.py
@@ -33,12 +33,11 @@ def read_data_from_db(cursor):
33 33
34 return list(results)34 return list(results)
3535
36def get_maas_session(maas_ip):36def get_maas_session(maas_ip, maas_admin, maas_admin_password):
37 client = login(37 client = login(
38 "http://{}:5240/MAAS/".format(maas_ip),38 "http://{}:5240/MAAS/".format(maas_ip),
39 username="ubuntu", password="ubuntu",39 username=maas_admin, password=maas_admin_password,
40 )40 )
41 print(type(client))
42 return client41 return client
4342
44def get_mac_list(machine_list):43def get_mac_list(machine_list):
@@ -63,7 +62,7 @@ def get_maas_mac_list(client):
63 62
64 return mac_list63 return mac_list
6564
66def create_maas_node(machine_list):65def create_maas_node(machine_list, maas_admin, maas_admin_password):
67 66
68 mass_is_connecting = ""67 mass_is_connecting = ""
69 client = ""68 client = ""
@@ -72,28 +71,28 @@ def create_maas_node(machine_list):
72 71
73 if not mass_is_connecting:72 if not mass_is_connecting:
74 mass_is_connecting = machine["MAAS_Server"]73 mass_is_connecting = machine["MAAS_Server"]
75 client = get_maas_session(mass_is_connecting)74 client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
76 maas_mac_list = get_maas_mac_list(client)75 maas_mac_list = get_maas_mac_list(client)
77 elif machine["MAAS_Server"] != mass_is_connecting:76 elif machine["MAAS_Server"] != mass_is_connecting:
78 mass_is_connecting = machine["MAAS_Server"]77 mass_is_connecting = machine["MAAS_Server"]
79 client = get_maas_session(mass_is_connecting)78 client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
80 maas_mac_list = get_maas_mac_list(client)79 maas_mac_list = get_maas_mac_list(client)
81 80
82 power_type = machine["Power"].lower() if machine["Power"] != "" else "manual"81 power_type = machine["Power"].lower() if machine["Power"] != "" else "manual"
83 power_parameter = {}82 power_parameter = {}
84 node_name = (machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \83 node_name = (machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
85 machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]).lower()84 machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]).lower()
85 node_name = machine["cid"].lower()
86 86
87 if machine["MAC"] in maas_mac_list:87 if machine["MAC"] in maas_mac_list or machine["MAC"] == '':
88 continue88 continue
89 if power_type == "apc" or power_type == "raritan":89 if power_type == "apc" or power_type == "raritan":
90 power_parameter["power_address"] = machine["PDU_IP"]90 power_parameter["power_address"] = machine["PDU_IP"]
91 power_parameter["node_outlet"] = machine["PDU_Outlet"]91 power_parameter["node_outlet"] = machine["PDU_Outlet"]
92 power_parameter["power_on_delay"] = 3092 power_parameter["power_on_delay"] = 30
93
94 elif power_type == "amt":93 elif power_type == "amt":
95 power_parameter["power_address"] = machine["IP"]94 power_parameter["power_address"] = machine["IP"]
96 power_parameter["power_pass"] = "Insecure_101"95 power_parameter["power_pass"] = "Insecure-101"
97 96
98 maas_node = client.machines.create("amd64",machine["MAC"], "manual",hostname=node_name)97 maas_node = client.machines.create("amd64",machine["MAC"], "manual",hostname=node_name)
99 98
@@ -111,11 +110,13 @@ def main():
111 110
112 parser = argparse.ArgumentParser()111 parser = argparse.ArgumentParser()
113 parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))112 parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
113 parser.add_argument('--maas_admin', help="MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN'))
114 parser.add_argument('--maas_admin_password', help="Password for MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN_PASSWD'))
114 args = parser.parse_args()115 args = parser.parse_args()
115 db = get_db_obj(args.database)116 db = get_db_obj(args.database)
116 cursor = get_cursor_obj(db)117 cursor = get_cursor_obj(db)
117 machine_list = read_data_from_db(cursor)118 machine_list = read_data_from_db(cursor)
118 create_maas_node(machine_list)119 create_maas_node(machine_list, args.maas_admin, args.maas_admin_password)
119 update_node_id(cursor)120 update_node_id(cursor)
120 close_db(db)121 close_db(db)
121122
diff --git a/cc_lab_manager.egg-info/PKG-INFO b/cc_lab_manager.egg-info/PKG-INFO
122new file mode 100644123new file mode 100644
index 0000000..03809d0
--- /dev/null
+++ b/cc_lab_manager.egg-info/PKG-INFO
@@ -0,0 +1,11 @@
1Metadata-Version: 1.1
2Name: cc-lab-manager
3Version: 0.1
4Summary: Automated managing hardwares in Canonical Certification Labs
5Home-page: https://code.launchpad.net/cc-lab-manager/
6Author: Gavin Lin (gavin.lin)
7Author-email: gavin.lin@canonical.com
8License: UNKNOWN
9Description: UNKNOWN
10Platform: UNKNOWN
11Classifier: Programming Language :: Python
diff --git a/cc_lab_manager.egg-info/SOURCES.txt b/cc_lab_manager.egg-info/SOURCES.txt
0new file mode 10064412new file mode 100644
index 0000000..7cfee1b
--- /dev/null
+++ b/cc_lab_manager.egg-info/SOURCES.txt
@@ -0,0 +1,23 @@
1README.md
2setup.cfg
3setup.py
4cc_lab_manager/__init__.py
5cc_lab_manager.egg-info/PKG-INFO
6cc_lab_manager.egg-info/SOURCES.txt
7cc_lab_manager.egg-info/dependency_links.txt
8cc_lab_manager.egg-info/entry_points.txt
9cc_lab_manager.egg-info/requires.txt
10cc_lab_manager.egg-info/top_level.txt
11cc_lab_manager/c3_db/__init__.py
12cc_lab_manager/c3_db/c3_db.py
13cc_lab_manager/commands/__init__.py
14cc_lab_manager/commands/cc_lab_manager.py
15cc_lab_manager/dhcp/__init__.py
16cc_lab_manager/dhcp/dhcp_config_generator.py
17cc_lab_manager/gen_config/__init__.py
18cc_lab_manager/gen_config/gen_agent_tf_config.py
19cc_lab_manager/gsheet_db/__init__.py
20cc_lab_manager/gsheet_db/db_gsheet.py
21cc_lab_manager/gsheet_db/gsheet_db.py
22cc_lab_manager/maas/__init__.py
23cc_lab_manager/maas/create_maas_node.py
0\ No newline at end of file24\ No newline at end of file
diff --git a/cc_lab_manager.egg-info/dependency_links.txt b/cc_lab_manager.egg-info/dependency_links.txt
1new file mode 10064425new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/cc_lab_manager.egg-info/dependency_links.txt
@@ -0,0 +1 @@
1
diff --git a/cc_lab_manager.egg-info/entry_points.txt b/cc_lab_manager.egg-info/entry_points.txt
0new file mode 1006442new file mode 100644
index 0000000..21bd2e3
--- /dev/null
+++ b/cc_lab_manager.egg-info/entry_points.txt
@@ -0,0 +1,3 @@
1[console_scripts]
2cc-lab-manager = cc_lab_manager.commands.cc_lab_manager:main
3
diff --git a/cc_lab_manager.egg-info/requires.txt b/cc_lab_manager.egg-info/requires.txt
0new file mode 1006444new file mode 100644
index 0000000..26dccbc
--- /dev/null
+++ b/cc_lab_manager.egg-info/requires.txt
@@ -0,0 +1,4 @@
1aiohttp==3.7.2
2jinja2
3pysheets
4python-libmaas
diff --git a/cc_lab_manager.egg-info/top_level.txt b/cc_lab_manager.egg-info/top_level.txt
0new file mode 1006445new file mode 100644
index 0000000..b831c50
--- /dev/null
+++ b/cc_lab_manager.egg-info/top_level.txt
@@ -0,0 +1 @@
1cc_lab_manager
diff --git a/cc_lab_manager/__init__.py b/cc_lab_manager/__init__.py
0new file mode 1006442new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc_lab_manager/__init__.py
diff --git a/cc_lab_manager/c3_db/__init__.py b/cc_lab_manager/c3_db/__init__.py
1new file mode 1006443new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc_lab_manager/c3_db/__init__.py
diff --git a/cc_lab_manager/c3_db/c3_db.py b/cc_lab_manager/c3_db/c3_db.py
2new file mode 1006444new file mode 100644
index 0000000..b9b0d7e
--- /dev/null
+++ b/cc_lab_manager/c3_db/c3_db.py
@@ -0,0 +1,128 @@
1import sqlite3
2import logging
3import argparse
4import c3.api.query as c3query
5from c3.api.api_utils import APIQuery
6import os
7
8logger = logging.getLogger('cc-lab-manager')
9
10
11def c3_to_db(db_path):
12 """
13 Query machine info from c3 and write to db.
14 """
15
16 # Read CID from db
17 try:
18 hwdb = sqlite3.connect(db_path)
19 cur = hwdb.execute('SELECT cid FROM lab_hw')
20 cid_list = cur.fetchall()
21 except Exception as e:
22 logger.error('Error when reading db: ', e)
23
24 for cid_db in cid_list:
25 cid = ''.join(cid_db)
26 logger.info('Processing {}...'.format(cid))
27 if '-' not in cid or 'TEL' in cid:
28 continue
29
30 # Query hardware info from c3
31 result = ''
32 try:
33 result = c3query.query_over_api_hardware(cid)
34 except Exception as e:
35 logger.error("Error when query c3: ", e)
36
37 if result:
38 # Map hardware info from c3 to db column
39 try:
40 location = result['location']['name']
41 except Exception as e:
42 logger.warning('Failed Reading location: {}'.format(e))
43 location = 'None'
44 try:
45 account = result['account']['name']
46 except Exception as e:
47 logger.warning('Failed reading account: {}'.format(e))
48 account = 'None'
49 try:
50 platform_name = result['platform']['name']
51 codename = result['platform']['codename']
52 form_factor = result['platform']['form_factor']
53 model = result['platform']['aliases']
54 except Exception as e:
55 logger.warning('Failed reading platform: {}'.format(e))
56 platform_name = 'None'
57 codename = 'None'
58 form_factor = 'None'
59 model = 'None'
60 try:
61 canonical_contact = result['canonical_contact']['display_name']
62 except Exception as e:
63 logger.warning('Failed reading canonical_contact: {}'
64 .format(e))
65 canonical_contact = 'None'
66 try:
67 holder = result['holder']['name']
68 except Exception as e:
69 logger.warning('Failed reading holder: {}'.format(e))
70 holder = 'None'
71 hw_info = {'Location': location,
72 'Status': result['status'],
73 'Account': account,
74 'Project': result['project_name'],
75 'Model': model,
76 'CanonicalLabel': result['canonical_label'],
77 'PlatformName': platform_name,
78 'CodeName': codename,
79 'SKU': result['sku'],
80 'HardwareBuild': result['hardware_build'],
81 'Form': form_factor,
82 'CanonicalContact': canonical_contact,
83 'Holder': holder,
84 'SecureID': result['secure_id']}
85
86 # Write hardware info to db
87 try:
88 for key in hw_info:
89 sql_cmd = 'UPDATE lab_hw SET {} = "{}" WHERE CID = "{}"' \
90 .format(key, hw_info[key],
91 result['canonical_id'])
92 hwdb.execute(sql_cmd)
93 hwdb.commit()
94 except Exception as e:
95 logger.error('Error writing to db: ', e)
96 hwdb.close()
97
98 hwdb.close()
99
100
101def environ_or_required(key):
102 if os.environ.get(key):
103 return {'default': os.environ.get(key)}
104 else:
105 return {'required': True}
106
107
108def main():
109 parser = argparse.ArgumentParser()
110 parser.add_argument('--conf', help="Config file for C3 API", default = os.environ.get('CCLM_CC_TOOL_BOX') + '/config/c3-cli/my_conf.ini')
111 parser.add_argument('--c3user', help="C3 User Account", **environ_or_required('CCLM_C3_USERNAME'))
112 parser.add_argument('--c3apikey', help="C3 API Key", **environ_or_required('CCLM_C3_APIKEY'))
113 parser.add_argument('--hwdb', help="C3 API Key", **environ_or_required('CCLM_HARDWARE_DB'))
114 args = parser.parse_args()
115
116 ci = c3query.configuration.get_instance()
117 ci.read_configuration(args.conf)
118
119 api = APIQuery(ci.config['C3']['URI'])
120 rparam = {"username": args.c3user,
121 "api_key": args.c3apikey}
122
123 c3query.api_instance.set_api_params(api, rparam)
124 c3_to_db(args.hwdb)
125
126
127if __name__ == '__main__':
128 main()
diff --git a/cc_lab_manager/commands/__init__.py b/cc_lab_manager/commands/__init__.py
0new file mode 100644129new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc_lab_manager/commands/__init__.py
diff --git a/cc_lab_manager/commands/cc_lab_manager.py b/cc_lab_manager/commands/cc_lab_manager.py
1new file mode 100644130new file mode 100644
index 0000000..7da03df
--- /dev/null
+++ b/cc_lab_manager/commands/cc_lab_manager.py
@@ -0,0 +1,49 @@
1import logging
2import os
3import sys
4
5from cc_lab_manager.gsheet_db import gsheet_db
6from cc_lab_manager.gsheet_db import db_gsheet
7from cc_lab_manager.c3_db import c3_db
8from cc_lab_manager.dhcp import dhcp_config_generator
9from cc_lab_manager.maas import create_maas_node
10from cc_lab_manager.gen_config import gen_agent_tf_config
11
12logger = logging.getLogger('cc-lab-manager')
13
14def main():
15 # Read data from the google sheet to db
16 gsheet_db.main()
17
18 # Get needed information from c3
19 c3_db.main()
20
21 # Generate DHCP config for each subnets
22 # TODO: Currently these config files need to be copied
23 # manually, then MAAS will apply it automatically
24 dhcp_config_generator.main()
25
26 # Create MAAS nodes for DUTs
27 create_maas_node.main()
28
29 # Create agent configs for DUTs
30 # TODO: Currently these configs need to be push/pull
31 # and applied manually
32 gen_agent_tf_config.main()
33
34 # Write data in db back to the google sheet
35 db_gsheet.main()
36
37 #ci = c3query.configuration.get_instance()
38
39 #api = APIQuery(ci.config['C3']['URI'])
40 #rparam = {"username": ci.config['C3']['UserName'],
41 # "api_key": ci.config['C3']['APIKey']}
42
43 #c3query.api_instance.set_api_params(api, rparam)
44 #hwdb = './hardware.db'
45 #c3_to_db(hwdb)
46
47
48if __name__ == '__main__':
49 main()
diff --git a/cc_lab_manager/dhcp/__init__.py b/cc_lab_manager/dhcp/__init__.py
0new file mode 10064450new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc_lab_manager/dhcp/__init__.py
diff --git a/cc_lab_manager/dhcp/dhcp_config_generator.py b/cc_lab_manager/dhcp/dhcp_config_generator.py
1new file mode 10064451new file mode 100644
index 0000000..e586dd4
--- /dev/null
+++ b/cc_lab_manager/dhcp/dhcp_config_generator.py
@@ -0,0 +1,96 @@
1import sqlite3
2import logging
3import argparse
4import os
5from jinja2 import Template
6
7logger = logging.getLogger('cc-lab-manager')
8
9
10def read_data_from_db(path_to_db):
11 db = sqlite3.connect(path_to_db)
12 db.row_factory = sqlite3.Row
13 col_in_db = ["CID", "MAC", "Controller_MAC", "Provision", "IP",
14 "Controller_IP", "Lab", "Frame", "Shelf", "Partition"]
15 sqlcmd = ('select {},{},{},{},{},{},{},{},{},{} from lab_hw'
16 .format(*col_in_db))
17 db_row_objs = db.execute(sqlcmd).fetchall()
18 db.close()
19 db_dict_list = []
20 for row_obj in db_row_objs:
21 db_dict_list.append({k: row_obj[k] for k in row_obj.keys()})
22 return db_dict_list
23
24
25class DhcpManager:
26 def __init__(self):
27 self.dhcp_template = '''
28# {{CID}}
29host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}} {
30 hardware ethernet {{MAC}};
31 fixed-address {{IP}};
32}
33
34# {{CID}}-controller
35host {{Lab}}-{{Frame}}-S{{Shelf}}-P{{Partition}}-ctl {
36 hardware ethernet {{Controller_MAC}};
37 fixed-address {{Controller_IP}};
38}
39
40'''
41
42 def generate_dhcp_conf(self, ip_mac_list, dhcp_conf_path):
43 dhcp_conf_data = {}
44 for ip_mac in ip_mac_list:
45 if ip_mac['IP'] == '' or ip_mac['Controller_IP'] == ''\
46 or ip_mac['Lab'] == '' or ip_mac['Frame'] == ''\
47 or ip_mac['Shelf'] == '' or ip_mac['Partition'] == '':
48 if ip_mac['CID'] == '':
49 ip_mac_list.remove(ip_mac)
50 else:
51 logger.info("Missing required information for", ip_mac['CID'])
52 continue
53
54 # Generate MAC for dummy devices or devices without fixed MAC
55 if ip_mac['MAC'] == '':
56 ip = ip_mac['IP'].split('.')
57 ip_mac['MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
58 f'{int(ip[2]):02x}:{int(ip[3]):02x}')
59 if ip_mac['Controller_MAC'] == '':
60 ip = ip_mac['Controller_IP'].split('.')
61 ip_mac['Controller_MAC'] = (f'ff:ff:ff:{int(ip[1]):02x}:'
62 f'{int(ip[2]):02x}:'
63 f'{int(ip[3]):02x}')
64
65 tm = Template(self.dhcp_template)
66 machine_ip = tm.render(ip_mac)
67 if ip_mac['Lab'] not in dhcp_conf_data:
68 dhcp_conf_data[ip_mac['Lab']] = machine_ip
69 else:
70 dhcp_conf_data[ip_mac['Lab']] += machine_ip
71
72 for lab_id in dhcp_conf_data:
73 with open(os.path.join(dhcp_conf_path, 'cert-' + lab_id.lower() + '-dhcpd-pool.conf'), 'w') as out:
74 out.write(dhcp_conf_data[lab_id])
75
76
77def environ_or_required(key):
78 if os.environ.get(key):
79 return {'default': os.environ.get(key)}
80 else:
81 return {'required': True}
82
83
84def main():
85 parser = argparse.ArgumentParser()
86 parser.add_argument('--cctoolbox', help="Path to cc-tool-box for storing dhcp config", **environ_or_required('CCLM_CC_TOOL_BOX'))
87 parser.add_argument('--hwdb', help="Hardware db to read ip mapping", **environ_or_required('CCLM_HARDWARE_DB'))
88 args = parser.parse_args()
89
90 ip_mac_list = read_data_from_db(args.hwdb)
91 dm = DhcpManager()
92 dm.generate_dhcp_conf(ip_mac_list, os.path.join(args.cctoolbox, 'config', 'dhcp'))
93
94
95if __name__ == '__main__':
96 main()
diff --git a/cc_lab_manager/gen_config/__init__.py b/cc_lab_manager/gen_config/__init__.py
0new file mode 10064497new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc_lab_manager/gen_config/__init__.py
diff --git a/cc_lab_manager/gen_config/gen_agent_tf_config.py b/cc_lab_manager/gen_config/gen_agent_tf_config.py
1new file mode 10064498new file mode 100644
index 0000000..8056656
--- /dev/null
+++ b/cc_lab_manager/gen_config/gen_agent_tf_config.py
@@ -0,0 +1,203 @@
1import sqlite3
2import os
3import yaml
4import argparse
5
6agent_config = {"device_ip": None,
7 "secure_id": None,
8 "node_id": None,
9 "maas_user": "cert_maas_admin",
10 "agent_name": None,
11 "node_name": None,
12 "reset_efi": "true",
13 "env":{"HEXR_DEVICE_SECURE_ID": None,
14 "WPA_BG_SSID": "ubuntu-cert-bg-wpa-tpelab",
15 "WPA_BG_PSK": "insecure",
16 "WPA_N_SSID": "ubuntu-cert-n-wpa-tpelab",
17 "WPA_N_PSK": "insecure",
18 "WPA_AC_SSID": "ubuntu-cert-ac-wpa-tpelab",
19 "WPA_AC_PSK": "insecure",
20 "OPEN_BG_SSID": "ubuntu-cert-bg-open-tpelab",
21 "OPEN_N_SSID": "ubuntu-cert-n-open-tpelab",
22 "OPEN_AC_SSID": "ubuntu-cert-ac-open-tpelab",
23 "DEVICE_IP": None
24 }
25 }
26
27tf_config = {"agent_id": None,
28 "server_address": "https://testflinger.canonical.com",
29 "global_timeout": "43200",
30 "output_timeout": "8000",
31 "execution_basedir": "/home/ubuntu/testflinger/{}/run",
32 "logging_basedir": "/home/ubuntu/testflinger/{}/logs",
33 "results_basedir": "/home/ubuntu/testflinger/{}/results",
34 "logging_level": "INFO",
35 "job_queues": None,
36 "setup_command": "tf-setup",
37 "provision_command": "tf-provision",
38 "test_command": "tf-test",
39 "reserve_command": "tf-reserve",
40 "cleanup_command": "tf-cleanup",
41 "provision_type": "maas2"
42 }
43
44def get_db_obj(path_to_db):
45
46 db = sqlite3.connect(path_to_db)
47 db.row_factory = sqlite3.Row
48 return db
49
50def get_cursor_obj(db_obj):
51
52 cursor = db_obj.cursor()
53 return cursor
54
55def close_db(db):
56 db.commit()
57 db.close()
58
59def read_data_from_db(cursor):
60
61 col_in_c3 = ["CID","Lab","IP","SKU","MAAS_Node_ID","Model","SecureID"]
62
63 sqlcmd = 'select {},{},{},{},{},{},{} from lab_hw where CID not like \'TEL-%\''.format(*col_in_c3)
64 cursor.execute(sqlcmd)
65 results = cursor.fetchall()
66 return list(results)
67
68def create_agent_config_dir(db_machine_list, cfg_path):
69
70 for machine in db_machine_list:
71
72 if machine['lab'] == '':
73 continue
74 agent_name = "dut"+machine["cid"].replace("-","")
75 try:
76 os.makedirs(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name))
77 except:
78 print("dir exist")
79
80def generate_agent_config(db_machine_list, cfg_path):
81
82 for machine in db_machine_list:
83
84 if machine['lab'] == '':
85 continue
86 agent_conf = agent_config.copy()
87 agent_name = "dut" + machine["cid"].replace("-","")
88 secure_id = machine["SecureID"]
89 ip = machine["IP"]
90 node_id = machine["MAAS_Node_ID"]
91 # node_name = machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
92 # machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]
93 node_name = machine["cid"]
94
95 agent_conf["node_name"] = node_name.lower()
96 agent_conf["agent_name"] = agent_name
97 agent_conf["secure_id"] = secure_id
98 agent_conf["node_id"] = node_id
99 agent_conf["device_ip"] = ip
100 agent_conf["env"]["HEXR_DEVICE_SECURE_ID"] = secure_id
101 agent_conf["env"]["DEVICE_IP"] = ip
102
103 with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'), "w+") as f:
104
105 for key in agent_config:
106 if key == "env":
107 f.write(key+" :\n")
108 for env_key in agent_config["env"]:
109 if "SSID" in env_key:
110 agent_conf["env"][env_key] += machine["lab"][-1]
111 f.write(" {key}: {value}\n".format(key=env_key,value=agent_conf["env"][env_key]))
112 else:
113 f.write("{key}: {value}\n".format(key=key,value=agent_conf[key]))
114
115
116def generate_tf_config(db_machine_list, cfg_path):
117
118 for machine in db_machine_list:
119 if machine['lab'] == '':
120 continue
121 tf_conf = tf_config.copy()
122
123 agent_name = "dut" + machine["cid"].replace("-","")
124 tf_conf["agent_id"] = agent_name
125 tf_conf["execution_basedir"] = tf_conf["execution_basedir"].format(agent_name)
126 tf_conf["logging_basedir"] = tf_conf["logging_basedir"].format(agent_name)
127 tf_conf["results_basedir"] = tf_conf["results_basedir"].format(agent_name)
128
129 with open(os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf'), "w+") as f:
130 for key in tf_conf:
131 if key == "job_queues":
132 f.write(key+":\n"+"- "+machine["cid"] + "\n")
133 else:
134 f.write(key + ": " + tf_conf[key] + "\n")
135
136def create_agent_yaml(db_machine_list, cfg_path):
137
138 # TODO: We need to separate configs for labs
139 if not os.path.isfile(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml")):
140
141 with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"w") as f:
142 data = {
143 "series": "focal",
144 "description": "cert-testflinger-agents",
145 "applications":{
146 "agent-host-server" + "-tel-l5":{
147 "series": "focal",
148 "constraints": "tags=agent-host" + "-tel-l5",
149 "charm": "../charms/testflinger-agent-host-charm",
150 "num_units": 1,
151 "resources":{
152 "ssh_priv_key": "agent-host/id_rsa",
153 "ssh_pub_key": "agent-host/id_rsa.pub",
154 },
155 }
156 }
157 }
158 yaml.dump(data, f, default_flow_style=False, encoding='utf-8', allow_unicode=True, sort_keys=False)
159
160 with open(os.path.join(cfg_path, "ce-tf-agents-tel-l5.yaml"),"r+") as f:
161
162 data = yaml.safe_load(f)
163 for machine in db_machine_list:
164 if machine['lab'] == '':
165 continue
166 agent_name = "dut" + machine["cid"].replace("-","")
167 if agent_name in data["applications"]:
168 print("exist")
169 else:
170 data["applications"][agent_name] = {
171 "charm":"../charms/testflinger-agent-charm",
172 "resources":{
173 "device_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'default.yaml'),
174 "testflinger_agent_configfile":os.path.join(cfg_path, 'data-' + machine['lab'].lower(), agent_name, 'testflinger-agent.conf')
175 },
176 "to": ["agent-host-server"]
177 }
178 f.seek(0)
179 yaml.dump(data, f, default_flow_style=False, encoding='utf-8', allow_unicode=True, sort_keys=False)
180
181
182def environ_or_required(key):
183 if os.environ.get(key):
184 return {'default': os.environ.get(key)}
185 else:
186 return {'required': True}
187
188def main():
189
190 parser = argparse.ArgumentParser()
191 parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
192 args = parser.parse_args()
193 db = get_db_obj(args.database)
194 cursor = get_cursor_obj(db)
195 machine_list = read_data_from_db(cursor)
196 create_agent_config_dir(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
197 generate_agent_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
198 generate_tf_config(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
199 create_agent_yaml(machine_list, os.path.join(os.environ.get('CCLM_CERT_JUJU'), 'ce-cert'))
200 close_db(db)
201
202if __name__ == '__main__':
203 main()
diff --git a/cc_lab_manager/gsheet_db/__init__.py b/cc_lab_manager/gsheet_db/__init__.py
0new file mode 100644204new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc_lab_manager/gsheet_db/__init__.py
diff --git a/cc_lab_manager/gsheet_db/db_gsheet.py b/cc_lab_manager/gsheet_db/db_gsheet.py
1new file mode 100644205new file mode 100644
index 0000000..c535445
--- /dev/null
+++ b/cc_lab_manager/gsheet_db/db_gsheet.py
@@ -0,0 +1,107 @@
1import pygsheets
2import sqlite3
3import sys
4from string import ascii_lowercase
5import itertools
6import argparse
7import os
8
9gsheet_url = ''
10start_column_name = 'Location'
11
12def get_db_obj(path_to_db):
13
14 db = sqlite3.connect(path_to_db)
15 db.row_factory = sqlite3.Row
16 return db
17
18def get_cursor_obj(db_obj):
19
20 cursor = db_obj.cursor()
21 return cursor
22
23def close_db(db):
24 db.commit()
25 db.close()
26
27def iter_all_strings():
28 # generate alphabet list a,b,c.........az for accessing cell's position.
29
30 col_position_list = []
31 for size in itertools.count(1):
32 for s in itertools.product(ascii_lowercase,repeat = size):
33 tmp_string = "".join(s)
34 col_position_list.append("".join(tmp_string))
35 if tmp_string == "az":
36 return col_position_list
37
38def get_column_to_be_updated(token_file):
39
40 gc = pygsheets.authorize(service_file=token_file)
41 sht = gc.open_by_url(gsheet_url)
42 wks = sht[0]
43 machine_list = wks.get_all_records(empty_value='', head=1, majdim='ROWS', numericise_data=False)
44 keylist = list(machine_list[0].keys())
45
46 started_index = keylist.index(start_column_name)
47 col_to_be_updated = dict.fromkeys(keylist[started_index:])
48 col_position_list = iter_all_strings()
49
50 for key in col_to_be_updated:
51 col_to_be_updated[key] = col_position_list[started_index]
52 started_index += 1
53
54 return col_to_be_updated
55
56
57def sync_c3_data_to_sheet(db_machine_list,token_file):
58
59 gc = pygsheets.authorize(service_file=token_file)
60 sht = gc.open_by_url(gsheet_url)
61 wks = sht[0]
62
63 # mapping table for position of column
64 col_to_be_updated = get_column_to_be_updated(token_file)
65
66 row_num = 2
67 cell_list = [] # list for storing cell that are waiting for update.
68 for machine in db_machine_list:
69 for key in col_to_be_updated:
70 cell_list.append(pygsheets.Cell(col_to_be_updated[key]+str(row_num),machine[key]))
71
72 row_num += 1
73 wks.update_cells(cell_list)
74
75
76def read_data_from_db(cursor, token_file):
77
78 col_in_c3 = list(get_column_to_be_updated(token_file).keys())
79 sqlcmd = 'select {} from lab_hw'.format(",".join(col_in_c3[0:]))
80 cursor.execute(sqlcmd)
81 results = cursor.fetchall()
82
83 return list(results)
84
85def environ_or_required(key):
86 if os.environ.get(key):
87 return {'default': os.environ.get(key)}
88 else:
89 return {'required': True}
90
91def main():
92
93 parser = argparse.ArgumentParser()
94 parser.add_argument('--url', help="Google sheets URL",**environ_or_required('CCLM_GOOGLE_SHEET'))
95 parser.add_argument('--token', help="Google API token",**environ_or_required('CCLM_GOOGLE_TOKEN'))
96 parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
97 args = parser.parse_args()
98 global gsheet_url
99 gsheet_url = args.url
100 db = get_db_obj(args.database)
101 cursor = get_cursor_obj(db)
102 sync_c3_data_to_sheet(read_data_from_db(cursor, args.token),args.token)
103 close_db(db)
104
105
106if __name__ == '__main__':
107 main()
diff --git a/cc_lab_manager/gsheet_db/gsheet_db.py b/cc_lab_manager/gsheet_db/gsheet_db.py
0new file mode 100644108new file mode 100644
index 0000000..1b11014
--- /dev/null
+++ b/cc_lab_manager/gsheet_db/gsheet_db.py
@@ -0,0 +1,91 @@
1import pygsheets
2import sqlite3
3import sys
4import argparse
5import os
6
7gsheet_url = ''
8
9def get_machine_list(token_file):
10
11 gc = pygsheets.authorize(service_file=token_file)
12 sht = gc.open_by_url(gsheet_url)
13 wks = sht[0]
14 machine_list = wks.get_all_records(empty_value='', head=1, majdim='ROWS', numericise_data=False)
15
16 return machine_list
17
18def get_db_obj(path_to_db):
19
20 db = sqlite3.connect(path_to_db)
21 return db
22
23def get_cursor_obj(db_obj):
24
25 cursor = db_obj.cursor()
26 return cursor
27
28def close_db(db):
29 db.commit()
30 db.close()
31
32def check_db_table_exist(cursor,table_name):
33
34 sqlcmd = 'select name from sqlite_master where type=\'table\' and name="{table_name}"'.format(table_name=table_name)
35 cursor.execute(sqlcmd)
36 result = cursor.fetchone()
37 return result
38
39def write_machine_list_to_db(cursor,token_file):
40
41 machine_list = get_machine_list(token_file)
42 keylist = [*machine_list[0]]
43 create_cmd = "CREATE TABLE lab_hw ({})".format(keylist[0]+ " PRIMARY KEY," +",".join(keylist[1:]))
44
45 #check table is exist or not
46 if check_db_table_exist(cursor,"lab_hw"):
47 print('table exist')
48 cursor.execute("DELETE FROM lab_hw")
49
50 else:
51 try:
52 cursor.execute(create_cmd)
53
54 except sqlite3.Error as er:
55 print('SQLite error: %s' % (' '.join(er.args)))
56
57 insert_cmd_fmt = 'insert into lab_hw values("{}")'
58
59 for machine in machine_list:
60 if machine["CID"] == "":
61 print("1")
62 machine["CID"] = machine["Lab"] + machine["Frame"] + machine["Shelf"] + machine["Partition"]
63 insert_cmd = insert_cmd_fmt.format('","'.join(machine.values()))
64 try:
65 cursor.execute(insert_cmd)
66
67 except sqlite3.Error as er:
68 print('SQLite error: %s' % (' '.join(er.args)))
69
70def environ_or_required(key):
71 if os.environ.get(key):
72 return {'default': os.environ.get(key)}
73 else:
74 return {'required': True}
75
76def main():
77
78 parser = argparse.ArgumentParser()
79 parser.add_argument('--url', help="Google sheets URL",**environ_or_required('CCLM_GOOGLE_SHEET'))
80 parser.add_argument('--token', help="Google API token",**environ_or_required('CCLM_GOOGLE_TOKEN'))
81 parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
82 args = parser.parse_args()
83 global gsheet_url
84 gsheet_url = args.url
85 db = get_db_obj(args.database)
86 cursor = get_cursor_obj(db)
87 write_machine_list_to_db(cursor,args.token)
88 close_db(db)
89
90if __name__ == '__main__':
91 main()
diff --git a/cc_lab_manager/maas/__init__.py b/cc_lab_manager/maas/__init__.py
0new file mode 10064492new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cc_lab_manager/maas/__init__.py
diff --git a/cc_lab_manager/maas/create_maas_node.py b/cc_lab_manager/maas/create_maas_node.py
1new file mode 10064493new file mode 100644
index 0000000..05be6dc
--- /dev/null
+++ b/cc_lab_manager/maas/create_maas_node.py
@@ -0,0 +1,124 @@
1from doctest import master
2from maas.client import connect
3from maas.client import login
4from maas.client.enum import LinkMode
5import argparse
6import os
7import sqlite3
8
9update_node_id_list = []
10
11def get_db_obj(path_to_db):
12
13 db = sqlite3.connect(path_to_db)
14 db.row_factory = sqlite3.Row
15 return db
16
17def get_cursor_obj(db_obj):
18
19 cursor = db_obj.cursor()
20 return cursor
21
22def close_db(db):
23 db.commit()
24 db.close()
25
26def read_data_from_db(cursor):
27
28 col_in_c3 = ["CID","MAC","Model","SKU","Power","IP","PDU_IP","PDU_Outlet","MAAS_Server","MAAS_Node_ID"]
29
30 sqlcmd = 'select {} from lab_hw where CID not like \'TEL-%\' order by MAAS_Server'.format(",".join(col_in_c3[0:]))
31 cursor.execute(sqlcmd)
32 results = cursor.fetchall()
33
34 return list(results)
35
36def get_maas_session(maas_ip, maas_admin, maas_admin_password):
37 client = login(
38 "http://{}:5240/MAAS/".format(maas_ip),
39 username=maas_admin, password=maas_admin_password,
40 )
41 return client
42
43def get_mac_list(machine_list):
44
45 mac_list = []
46 for machine in machine_list:
47 mac_list.append(machine["mac"])
48
49 return mac_list
50
51def update_node_id(cursor):
52
53 sqlcmd = 'update lab_hw set MAAS_Node_ID=? where CID=?'
54 cursor.executemany(sqlcmd, update_node_id_list)
55
56def get_maas_mac_list(client):
57
58 mac_list = []
59 for machine in client.machines.list():
60 for key in machine.interfaces.by_name:
61 mac_list.append(machine.interfaces.by_name[key].mac_address)
62
63 return mac_list
64
65def create_maas_node(machine_list, maas_admin, maas_admin_password):
66
67 mass_is_connecting = ""
68 client = ""
69
70 for machine in machine_list:
71
72 if not mass_is_connecting:
73 mass_is_connecting = machine["MAAS_Server"]
74 client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
75 maas_mac_list = get_maas_mac_list(client)
76 elif machine["MAAS_Server"] != mass_is_connecting:
77 mass_is_connecting = machine["MAAS_Server"]
78 client = get_maas_session(mass_is_connecting, maas_admin, maas_admin_password)
79 maas_mac_list = get_maas_mac_list(client)
80
81 power_type = machine["Power"].lower() if machine["Power"] != "" else "manual"
82 power_parameter = {}
83 node_name = (machine["Model"].replace(")","",).replace("(","",).replace(" ","-")+ "-" + \
84 machine["sku"].replace(" ","").split("-")[-1] + "-" + machine["cid"]).lower()
85 node_name = machine["cid"].lower()
86
87 if machine["MAC"] in maas_mac_list or machine["MAC"] == '':
88 continue
89 if power_type == "apc" or power_type == "raritan":
90 power_parameter["power_address"] = machine["PDU_IP"]
91 power_parameter["node_outlet"] = machine["PDU_Outlet"]
92 power_parameter["power_on_delay"] = 30
93 elif power_type == "amt":
94 power_parameter["power_address"] = machine["IP"]
95 power_parameter["power_pass"] = "Insecure-101"
96
97 maas_node = client.machines.create("amd64",machine["MAC"], "manual",hostname=node_name)
98
99 update_node_id_list.append([maas_node.system_id, machine["CID"]])
100 maas_node.set_power(power_type=power_type.lower(),power_parameters=power_parameter)
101
102
103def environ_or_required(key):
104 if os.environ.get(key):
105 return {'default': os.environ.get(key)}
106 else:
107 return {'required': True}
108
109def main():
110
111 parser = argparse.ArgumentParser()
112 parser.add_argument('--database', help="Database location",**environ_or_required('CCLM_HARDWARE_DB'))
113 parser.add_argument('--maas_admin', help="MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN'))
114 parser.add_argument('--maas_admin_password', help="Password for MAAS Admin account",**environ_or_required('CCLM_MAAS_ADMIN_PASSWD'))
115 args = parser.parse_args()
116 db = get_db_obj(args.database)
117 cursor = get_cursor_obj(db)
118 machine_list = read_data_from_db(cursor)
119 create_maas_node(machine_list, args.maas_admin, args.maas_admin_password)
120 update_node_id(cursor)
121 close_db(db)
122
123if __name__ == '__main__':
124 main()
diff --git a/dist/cc_lab_manager-0.1-py3.8.egg b/dist/cc_lab_manager-0.1-py3.8.egg
0new file mode 100644125new file mode 100644
index 0000000..cd9a0a4
1Binary files /dev/null and b/dist/cc_lab_manager-0.1-py3.8.egg differ126Binary files /dev/null and b/dist/cc_lab_manager-0.1-py3.8.egg differ
diff --git a/setup.py b/setup.py
index a609291..d39888c 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
1#!/usr/bin/env python31#!/usr/bin/env python3
2from setuptools import setup, find_packages2from setuptools import setup, find_packages
33
4INSTALL_REQUIRES = ['pysheets']4INSTALL_REQUIRES = ['pysheets', 'jinja2', 'python-libmaas', 'aiohttp==3.7.2']
55
6setup(6setup(
7 name='cc-lab-manager',7 name='cc-lab-manager',
@@ -12,4 +12,12 @@ setup(
12 author_email='gavin.lin@canonical.com',12 author_email='gavin.lin@canonical.com',
13 url='https://code.launchpad.net/cc-lab-manager/',13 url='https://code.launchpad.net/cc-lab-manager/',
14 install_requires=INSTALL_REQUIRES,14 install_requires=INSTALL_REQUIRES,
15 entry_points={
16 'console_scripts': [
17 'cc-lab-manager=cc_lab_manager.commands.cc_lab_manager:main',
18 ]
19 },
20 classifiers=[
21 "Programming Language :: Python",
22 ]
15)23)

Subscribers

People subscribed via source and target branches

to all changes: