From 01dada100a34f42520446a64e02bd8eb2cf4e7fe Mon Sep 17 00:00:00 2001 From: Frank Zhang Date: Mon, 6 Oct 2014 15:46:36 -0700 Subject: [PATCH] CLOUDSTACK-6278 Baremetal Advanced Networking support --- .gitignore | 1 - .../BaremetalKickStartServiceImpl.java | 4 +- systemvm/patches/debian/config/etc/rc.local | 2 +- .../config/opt/cloud/bin/baremetal-vr.py | 145 ++++++++++++++++++ .../config/opt/cloud/bin}/baremetal_snat.sh | 0 .../config/opt/cloud/bin}/prepare_pxe.sh | 0 6 files changed, 148 insertions(+), 4 deletions(-) create mode 100755 systemvm/patches/debian/config/opt/cloud/bin/baremetal-vr.py rename {scripts/network/ping => systemvm/patches/debian/config/opt/cloud/bin}/baremetal_snat.sh (100%) rename {scripts/network/ping => systemvm/patches/debian/config/opt/cloud/bin}/prepare_pxe.sh (100%) diff --git a/.gitignore b/.gitignore index fdac72ea252..4ce64ef4e28 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ build/replace.properties build/build.number -bin/ .lock-wscript .waf-* waf-* diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java index 16fc4600925..7f6b0bf3f6a 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalKickStartServiceImpl.java @@ -230,7 +230,7 @@ public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase imple } List tuple = parseKickstartUrl(profile); - String cmd = String.format("/usr/bin/prepare_pxe.sh %s %s %s %s %s %s", tuple.get(1), tuple.get(2), profile.getTemplate().getUuid(), + String cmd = String.format("/opt/cloud/bin/prepare_pxe.sh %s %s %s %s %s %s", tuple.get(1), tuple.get(2), profile.getTemplate().getUuid(), String.format("01-%s", nic.getMacAddress().replaceAll(":", "-")).toLowerCase(), tuple.get(0), nic.getMacAddress().toLowerCase()); s_logger.debug(String.format("prepare pxe on virtual router[ip:%s], cmd: %s", mgmtNic.getIp4Address(), cmd)); Pair ret = SshHelper.sshExecute(mgmtNic.getIp4Address(), 3922, "root", getSystemVMKeyFile(), null, cmd); @@ -239,7 +239,7 @@ public class BaremetalKickStartServiceImpl extends BareMetalPxeServiceBase imple } //String internalServerIp = "10.223.110.231"; - cmd = String.format("/usr/bin/baremetal_snat.sh %s %s %s", mgmtNic.getIp4Address(), internalServerIp, mgmtNic.getGateway()); + cmd = String.format("/opt/cloud/bin/baremetal_snat.sh %s %s %s", mgmtNic.getIp4Address(), internalServerIp, mgmtNic.getGateway()); s_logger.debug(String.format("prepare SNAT on virtual router[ip:%s], cmd: %s", mgmtNic.getIp4Address(), cmd)); ret = SshHelper.sshExecute(mgmtNic.getIp4Address(), 3922, "root", getSystemVMKeyFile(), null, cmd); if (!ret.first()) { diff --git a/systemvm/patches/debian/config/etc/rc.local b/systemvm/patches/debian/config/etc/rc.local index 78fb5dea293..23e913e40b5 100755 --- a/systemvm/patches/debian/config/etc/rc.local +++ b/systemvm/patches/debian/config/etc/rc.local @@ -42,7 +42,7 @@ then echo 1000000 > /proc/sys/net/nf_conntrack_max fi -python /usr/bin/baremetal-vr.py & +python /opt/cloud/bin/baremetal-vr.py & date > /var/cache/cloud/boot_up_done logger -t cloud "Boot up process done" diff --git a/systemvm/patches/debian/config/opt/cloud/bin/baremetal-vr.py b/systemvm/patches/debian/config/opt/cloud/bin/baremetal-vr.py new file mode 100755 index 00000000000..05188d4099f --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/baremetal-vr.py @@ -0,0 +1,145 @@ +__author__ = 'frank' + +import subprocess +import urllib +import hmac +import hashlib +import base64 +import traceback +import logging + +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/', methods=['GET']) +def notify_provisioning_done(mac): + try: + return server.notify_provisioning_done(mac) + except: + logger.warn(traceback.format_exc()) + return '' + + +if __name__ == '__main__': + global server + 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, debug=True) diff --git a/scripts/network/ping/baremetal_snat.sh b/systemvm/patches/debian/config/opt/cloud/bin/baremetal_snat.sh similarity index 100% rename from scripts/network/ping/baremetal_snat.sh rename to systemvm/patches/debian/config/opt/cloud/bin/baremetal_snat.sh diff --git a/scripts/network/ping/prepare_pxe.sh b/systemvm/patches/debian/config/opt/cloud/bin/prepare_pxe.sh similarity index 100% rename from scripts/network/ping/prepare_pxe.sh rename to systemvm/patches/debian/config/opt/cloud/bin/prepare_pxe.sh