mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
168 lines
5.7 KiB
Python
Executable File
168 lines
5.7 KiB
Python
Executable File
#Licensed to the Apache Software Foundation (ASF) under one
|
|
#or more contributor license agreements. See the NOTICE file
|
|
#distributed with this work for additional information
|
|
#regarding copyright ownership. The ASF licenses this file
|
|
#to you 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 subprocess
|
|
import urllib
|
|
import hmac
|
|
import hashlib
|
|
import base64
|
|
import traceback
|
|
import logging
|
|
import re
|
|
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
logger = logging.getLogger('baremetal-vr')
|
|
hdlr = logging.FileHandler('/var/log/baremetal-vr.log')
|
|
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
|
hdlr.setFormatter(formatter)
|
|
logger.addHandler(hdlr)
|
|
logger.setLevel(logging.WARNING)
|
|
|
|
class ShellCmd(object):
|
|
'''
|
|
classdocs
|
|
'''
|
|
def __init__(self, cmd, workdir=None, pipe=True):
|
|
'''
|
|
Constructor
|
|
'''
|
|
self.cmd = cmd
|
|
if pipe:
|
|
self.process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, executable='/bin/sh', cwd=workdir)
|
|
else:
|
|
self.process = subprocess.Popen(cmd, shell=True, executable='/bin/sh', cwd=workdir)
|
|
|
|
self.stdout = None
|
|
self.stderr = None
|
|
self.return_code = None
|
|
|
|
def __call__(self, is_exception=True):
|
|
(self.stdout, self.stderr) = self.process.communicate()
|
|
if is_exception and self.process.returncode != 0:
|
|
err = []
|
|
err.append('failed to execute shell command: %s' % self.cmd)
|
|
err.append('return code: %s' % self.process.returncode)
|
|
err.append('stdout: %s' % self.stdout)
|
|
err.append('stderr: %s' % self.stderr)
|
|
raise Exception('\n'.join(err))
|
|
|
|
self.return_code = self.process.returncode
|
|
return self.stdout
|
|
|
|
def shell(cmd):
|
|
return ShellCmd(cmd)()
|
|
|
|
|
|
class Server(object):
|
|
CMDLINE = '/var/cache/cloud/cmdline'
|
|
def __init__(self):
|
|
self.apikey = None
|
|
self.secretkey = None
|
|
self.mgmtIp = None
|
|
self.mgmtPort = None
|
|
|
|
def _get_credentials(self):
|
|
if not self.apikey or not self.secretkey:
|
|
with open(self.CMDLINE, 'r') as fd:
|
|
cmdline = fd.read()
|
|
for p in cmdline.split():
|
|
if 'baremetalnotificationsecuritykey' in p:
|
|
self.secretkey = p.split("=")[1]
|
|
if 'baremetalnotificationapikey' in p:
|
|
self.apikey = p.split("=")[1]
|
|
|
|
if not self.apikey:
|
|
raise Exception('cannot find baremetalnotificationapikey in %s' % Server.CMDLINE)
|
|
if not self.secretkey:
|
|
raise Exception('cannot find baremetalnotificationsecuritykey in %s' % Server.CMDLINE)
|
|
|
|
return self.apikey, self.secretkey
|
|
|
|
def _get_mgmt_ip(self):
|
|
if not self.mgmtIp:
|
|
with open(self.CMDLINE, 'r') as fd:
|
|
cmdline = fd.read()
|
|
for p in cmdline.split():
|
|
if 'host' in p:
|
|
self.mgmtIp = p.split("=")[1]
|
|
break
|
|
|
|
if not self.mgmtIp:
|
|
raise Exception('cannot find host in %s' % Server.CMDLINE)
|
|
|
|
return self.mgmtIp
|
|
|
|
def _get_mgmt_port(self):
|
|
if not self.mgmtPort:
|
|
with open(self.CMDLINE, 'r') as fd:
|
|
cmdline = fd.read()
|
|
for p in cmdline.split():
|
|
if 'port' in p:
|
|
self.mgmtPort = p.split("=")[1]
|
|
break
|
|
|
|
if not self.mgmtIp:
|
|
raise Exception('cannot find port in %s' % Server.CMDLINE)
|
|
|
|
return self.mgmtPort
|
|
|
|
def _make_sign(self, mac):
|
|
apikey, secretkey = self._get_credentials()
|
|
reqs = {
|
|
"apiKey": apikey,
|
|
"command": 'notifyBaremetalProvisionDone',
|
|
"mac": mac
|
|
}
|
|
|
|
request = zip(reqs.keys(), reqs.values())
|
|
request.sort(key=lambda x: str.lower(x[0]))
|
|
hashStr = "&".join(["=".join([str.lower(r[0]), str.lower(urllib.quote_plus(str(r[1]))).replace("+", "%20").replace('=', '%3d')]) for r in request])
|
|
sig = urllib.quote_plus(base64.encodestring(hmac.new(secretkey, hashStr, hashlib.sha1).digest()).strip())
|
|
return sig
|
|
|
|
def notify_provisioning_done(self, mac):
|
|
sig = self._make_sign(mac)
|
|
cmd = 'http://%s:%s/client/api?command=notifyBaremetalProvisionDone&mac=%s&apiKey=%s&signature=%s' % (self._get_mgmt_ip(), self._get_mgmt_port(), mac, self.apikey, sig)
|
|
shell("curl -X GET '%s'" % cmd)
|
|
return ''
|
|
|
|
server = None
|
|
|
|
@app.route('/baremetal/provisiondone/<mac>', methods=['GET'])
|
|
def notify_provisioning_done(mac):
|
|
try:
|
|
if not is_a_mac(mac):
|
|
raise "there is an issue with that '%s'. Not a mac?" % mac
|
|
return server.notify_provisioning_done(mac)
|
|
except:
|
|
logger.warn(traceback.format_exc())
|
|
return ''
|
|
|
|
def is_a_mac(mac):
|
|
if re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower()):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
if __name__ == '__main__':
|
|
server = Server()
|
|
shell("iptables-save | grep -- '-A INPUT -i eth0 -p tcp -m tcp --dport 10086 -j ACCEPT' > /dev/null || iptables -I INPUT -i eth0 -p tcp -m tcp --dport 10086 -j ACCEPT")
|
|
app.run(host='0.0.0.0', port=10086)
|