summaryrefslogtreecommitdiffstats
path: root/laas-fog
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2017-08-21 09:03:53 -0400
committerParker Berberian <pberberian@iol.unh.edu>2017-08-31 13:28:57 -0400
commitefb7362691b1103513dad6c098994a6ed1929ab2 (patch)
tree274f3e0a31804b335abd8838571c6b421d394da6 /laas-fog
parent3681849c3ab451145e8c79996d54e10a38d5484a (diff)
Adds the Free and Open-source Ghost (FOG)
JIRA: N/A Adds a handler which will talk with the REST api running on the FOG server. This allows the pod_manager to ghost images onto hosts in order to prep them for deployment and clean them afterwards. Change-Id: Ic00e992874ca3371b6d6e8ac2450a1ef0c765e67 Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
Diffstat (limited to 'laas-fog')
-rw-r--r--laas-fog/source/api/__init__.py17
-rw-r--r--laas-fog/source/api/fog.py288
2 files changed, 305 insertions, 0 deletions
diff --git a/laas-fog/source/api/__init__.py b/laas-fog/source/api/__init__.py
new file mode 100644
index 0000000..7bb515b
--- /dev/null
+++ b/laas-fog/source/api/__init__.py
@@ -0,0 +1,17 @@
+"""
+#############################################################################
+#Copyright 2017 Parker Berberian and others #
+# #
+#Licensed under the Apache License, Version 2.0 (the "License"); #
+#you may not use this file except in compliance with the License. #
+#You may obtain a copy of the License at #
+# #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+# #
+#Unless required by applicable law or agreed to in writing, software #
+#distributed under the License is distributed on an "AS IS" BASIS, #
+#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+#See the License for the specific language governing permissions and #
+#limitations under the License. #
+#############################################################################
+"""
diff --git a/laas-fog/source/api/fog.py b/laas-fog/source/api/fog.py
new file mode 100644
index 0000000..6287403
--- /dev/null
+++ b/laas-fog/source/api/fog.py
@@ -0,0 +1,288 @@
+"""
+#############################################################################
+#Copyright 2017 Parker Berberian and others #
+# #
+#Licensed under the Apache License, Version 2.0 (the "License"); #
+#you may not use this file except in compliance with the License. #
+#You may obtain a copy of the License at #
+# #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+# #
+#Unless required by applicable law or agreed to in writing, software #
+#distributed under the License is distributed on an "AS IS" BASIS, #
+#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+#See the License for the specific language governing permissions and #
+#limitations under the License. #
+#############################################################################
+"""
+
+import requests
+import sys
+import time
+
+
+class FOG_Handler:
+ """
+ This class talks with the REST web api for the FOG server.
+
+ TODO: convert prints to logs and remove uneeded pass's
+ """
+
+ def __init__(self, baseURL, fogKey=None, userKey=None):
+ """
+ init function
+ baseURL should be http://fog.ip.or.hostname/fog/
+ fogKey and userKey can optionally be supplied here or later
+ They can be found in fog and provide authentication.
+ """
+ self.baseURL = baseURL
+ self.fogKey = fogKey
+ self.userKey = userKey
+ self.header = {}
+ self.updateHeader()
+
+ def setLogger(self, logger):
+ """
+ saves the refference to the log object as
+ self.log
+ """
+ self.log = logger
+
+ def getUserKeyFromFile(self, path):
+ """
+ reads the user api key from a file
+ """
+ self.userKey = open(path).read()
+ self.updateHeader()
+
+ def getFogKeyFromFile(self, path):
+ """
+ reads the api key from a file
+ """
+ self.fogKey = open(path).read()
+ self.updateHeader()
+
+ def setUserKey(self, key):
+ """
+ sets the user key
+ """
+ self.userKey = key
+ self.updateHeader()
+
+ def setFogKey(self, key):
+ """
+ sets the fog key
+ """
+ self.fogKey = key
+ self.updateHeader()
+
+ def updateHeader(self):
+ """
+ recreates the http header used to talk to the fog api
+ """
+ self.header = {}
+ self.header['fog-api-token'] = self.fogKey
+ self.header['fog-user-token'] = self.userKey
+
+ def setImage(self, host, imgNum):
+ """
+ Sets the image to be used during ghosting to the image
+ with id imgNum. host can either be a hostname or number.
+ """
+ try:
+ host = int(host)
+ except:
+ host = self.getHostNumber(host)
+ url = self.baseURL+"host/"+str(host)
+ host_conf = requests.get(url, headers=self.header).json()
+ host_conf['imageID'] = str(imgNum)
+ requests.put(url+"/edit", headers=self.header, json=host_conf)
+
+ def delTask(self, hostNum):
+ """
+ Tries to delete an existing task for the host
+ with hostNum as a host number
+ """
+ try:
+ url = self.baseURL+'fog/host/'+str(hostNum)+'/cancel'
+ req = requests.delete(url, headers=self.header)
+ if req.status_code == 200:
+ self.log.info("%s", "successfully deleted image task")
+ except Exception:
+ self.log.exception("Failed to delete the imaging task!")
+
+ def getHostMac(self, hostname):
+ """
+ returns the primary mac address if the given host.
+ """
+ try:
+ hostNum = int(self.getHostNumber(hostname))
+ url = self.baseURL + "host/"+str(hostNum)
+ req = requests.get(url, headers=self.header)
+ macAddr = req.json()['primac']
+ return macAddr
+ except Exception:
+ self.log.exception('%s', "Failed to connect to the FOG server")
+
+ def getHostNumber(self, hostname):
+ """
+ returns the host number of given host
+ """
+ try:
+ req = requests.get(self.baseURL+"host", headers=self.header)
+ hostData = req.json()
+ if hostData is not None:
+ for hostDict in hostData['hosts']:
+ if hostname == hostDict['name']:
+ return hostDict['id']
+ return -1
+ except Exception:
+ self.log.exception('%s', "Failed to connect to the FOG server")
+
+ def imageHost(self, hostName, recurse=False):
+ """
+ Schedules an imaging task for the given host.
+ This automatically uses the "associated" disk image.
+ To support extra installers, I will need to create
+ a way to change what that image is before calling
+ this method.
+ """
+ num = str(self.getHostNumber(hostName))
+ url = self.baseURL+'host/'+num+'/task'
+
+ try:
+ req = requests.post(
+ url,
+ headers=self.header,
+ json={"taskTypeID": 1}
+ )
+ if req.status_code == 200:
+ self.log.info("%s", "Scheduled image task for host")
+ except Exception:
+ if recurse: # prevents infinite loop
+ self.log.exception("%s", "Failed to schedule task. Exiting")
+ sys.exit(1)
+ self.log.warning("%s", "Failed to schedule host imaging")
+ self.log.warning("%s", "Trying to delete existing image task")
+ self.delTask(num)
+ self.imageHost(num, recurse=True)
+
+ def waitForHost(self, host):
+ """
+ tracks the imaging task to completion.
+ """
+ while True:
+ imageTask = self.getImagingTask(host)
+ if imageTask is None:
+ self.log.info("%s", "Imaging complete")
+ return
+ state = int(imageTask['stateID'])
+ if state == 1:
+ self.log.info("%s", "Waiting for host to check in")
+ self.waitForTaskToActive(host)
+ continue
+ if state == 3:
+ self.waitForTaskToStart(host)
+ self.waitForImaging(host)
+ continue
+ time.sleep(8)
+
+ def waitForImaging(self, host):
+ """
+ Once the host begins being imaged, this tracks progress.
+ """
+ # print "Host has begun the imaging process\n"
+ while True:
+ task = self.getImagingTask(host)
+ if task is None:
+ return
+ per = str(task['percent'])
+ self.log.info("%s percent done imaging", per)
+ time.sleep(15)
+
+ def waitForTaskToActive(self, host):
+ """
+ Waits for the host to reboot and pxe boot
+ into FOG
+ """
+ while True:
+ try:
+ task = self.getImagingTask(host)
+ except:
+ pass
+ state = int(task['stateID'])
+ if state == 1:
+ time.sleep(4)
+ else:
+ return
+
+ def waitForTaskToStart(self, host):
+ """
+ waits for the task to start and imaging to begin.
+ """
+ while True:
+ try:
+ per = str(self.getImagingTask(host)['percent'])
+ except:
+ pass
+ if per.strip() == '':
+ time.sleep(1)
+ else:
+ return
+
+ def getImagingTask(self, host):
+ """
+ Sorts through all current tasks to find the image task
+ associated with the given host.
+ """
+ try:
+ taskList = requests.get(
+ self.baseURL+'task/current',
+ headers=self.header)
+ taskList = taskList.json()['tasks']
+ imageTask = None
+ for task in taskList:
+ hostname = str(task['host']['name'])
+ if hostname == host and int(task['typeID']) == 1:
+ imageTask = task
+ return imageTask
+ except Exception:
+ self.log.exception("%s", "Failed to talk to FOG server")
+ sys.exit(1)
+
+ def getHosts(self):
+ """
+ returns a list of all hosts
+ """
+ req = requests.get(self.baseURL+"host", headers=self.header)
+ return req.json()['hosts']
+
+ def getHostsinGroup(self, groupName):
+ """
+ returns a list of all hosts in groupName
+ """
+ groupID = None
+ groups = requests.get(self.baseURL+"group", headers=self.header)
+ groups = groups.json()['groups']
+ for group in groups:
+ if groupName.lower() in group['name'].lower():
+ groupID = group['id']
+ if groupID is None:
+ return
+ hostIDs = []
+ associations = requests.get(
+ self.baseURL+"groupassociation",
+ headers=self.header
+ )
+ associations = associations.json()['groupassociations']
+ for association in associations:
+ if association['groupID'] == groupID:
+ hostIDs.append(association['hostID'])
+
+ hosts = []
+ for hostID in hostIDs:
+ hosts.append(requests.get(
+ self.baseURL+"host/"+str(hostID),
+ headers=self.header
+ ).json())
+ return hosts