Ec2MetaServer.py.in 9.51 KB
Newer Older
1
#!/usr/local/bin/python
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#
# Copyright (c) 2012-2013 University of Utah and the Flux Group.
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
from BaseHTTPServer import HTTPServer
Srikanth Raju's avatar
Srikanth Raju committed
25 26
from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse
Srikanth Raju's avatar
Srikanth Raju committed
27
import traceback
Srikanth Raju's avatar
Srikanth Raju committed
28
import os
Srikanth Raju's avatar
Srikanth Raju committed
29
import sys
30
import syslog
31 32
import socket
import re
33 34 35 36 37 38

# Configure variables
TBDIR = "@prefix@"
TBFACIL = "@TBLOGFACIL@"
AVAIL = "@THISHOMEBASE@"

39 40 41
# When debugging, runs in foreground printing to stdout instead of syslog
debug = 0

42 43 44 45 46 47 48
TBPATH = os.path.join(TBDIR, "lib")
if TBPATH not in sys.path:
    sys.path.append(TBPATH)
    pass

from libdb        import *

49
# This requires Python 2.6+
Srikanth Raju's avatar
Srikanth Raju committed
50 51 52 53 54
class Ec2MetaHandler(BaseHTTPRequestHandler):

    def __init__(self, req, ca, huh):
        BaseHTTPRequestHandler.__init__(self,req,ca,huh)

55 56 57 58 59 60 61 62 63
    ##
    # Log a message to stdout, if in debug mode, otherwise write to syslog.
    #
    # @param msg The message to log.
    #
    def logit(self, msg):
        if debug:
            print msg
            pass
64
        else:
65 66 67
            syslog.syslog(syslog.LOG_INFO, msg);
            pass
        return
68

69 70 71 72
    # Override so we can use above function.
    def log_message(self, format, *args):
        self.logit(format%args)
        return
Srikanth Raju's avatar
Srikanth Raju committed
73

Srikanth Raju's avatar
Srikanth Raju committed
74 75 76 77 78 79 80 81
    def do_GET(self):
        parsed_path = urlparse.urlparse(self.path)

        only_path = parsed_path.path
        folders=[]
        while 1:
            only_path,folder=os.path.split(only_path)

82 83 84 85
            if folder != "":
                folders.append(folder)
            if only_path=="/":
                break;
Srikanth Raju's avatar
Srikanth Raju committed
86

87 88
        if len(folders) > 0:
            folders.pop() #Ignore version
Srikanth Raju's avatar
Srikanth Raju committed
89 90
        folders.reverse()

Srikanth Raju's avatar
Srikanth Raju committed
91 92 93 94
        try:
            message = self.handle_req(folders, self.metas)
            message = message + "\n"
        except Exception as e:
95
            syslog.syslog(traceback.format_exc())
Srikanth Raju's avatar
Srikanth Raju committed
96 97 98 99 100 101 102 103 104
            self.send_response(404)
            self.end_headers()
            return

        self.send_response(200)
        self.end_headers()
        self.wfile.write(message)
        return

Srikanth Raju's avatar
Srikanth Raju committed
105
    def listmetas(self, metas):
106 107 108 109
        message = "\n".join(map(lambda x: x + "/"
                                if (x == "public-keys" or
                                    not(callable(metas[x])))
                                else x, metas.keys()));
Srikanth Raju's avatar
Srikanth Raju committed
110
        return message
Srikanth Raju's avatar
Srikanth Raju committed
111

Srikanth Raju's avatar
Srikanth Raju committed
112 113 114 115 116 117 118
    def handle_req(self, arg, metas):
        if callable(metas):
            return metas(self, arg)
        elif len(arg) == 0:
            return self.listmetas(metas);
        else:
            return self.handle_req(arg[1:], metas[arg[0]])
Srikanth Raju's avatar
Srikanth Raju committed
119

120
    def do_userdata(self, arg):
Srikanth Raju's avatar
Srikanth Raju committed
121 122
        #TODO
        return "\n";
Srikanth Raju's avatar
Srikanth Raju committed
123

Srikanth Raju's avatar
Srikanth Raju committed
124 125
    def doamiid(self, arg):
        ip = self.client_address[0]
126
        rows = DBQueryWarn("select osname from os_info "
Srikanth Raju's avatar
Srikanth Raju committed
127 128 129
            "join nodes on os_info.osid = nodes.osid "
            "join interfaces on nodes.node_id=interfaces.node_id "
            "where interfaces.ip=%s", (ip,));
130 131
        if len(rows) > 0:
            ami_id = rows[0]
Srikanth Raju's avatar
Srikanth Raju committed
132 133 134 135
            ami_id = ami_id[0]
        else:
            ami_id = ""
        return ami_id;
Srikanth Raju's avatar
Srikanth Raju committed
136 137 138

    def dolocal_hostname(self, args):
        ip = self.client_address[0]
139 140
        rows = DBQueryWarn("select vname,eid,pid from reserved join interfaces "
            "on interfaces.node_id=reserved.node_id"
Srikanth Raju's avatar
Srikanth Raju committed
141
            " where interfaces.ip=%s",(ip,))
142 143
        if len(rows) > 0:
            node_id = rows[0]
Srikanth Raju's avatar
Srikanth Raju committed
144 145 146 147 148
        else:
            return ""

        return node_id[0] + "." + node_id[1] + "." + node_id[2] + "." + "emulab.net"

Srikanth Raju's avatar
Srikanth Raju committed
149
    def doavail(self, args):
150
        return AVAIL
Srikanth Raju's avatar
Srikanth Raju committed
151 152 153 154 155

    def domacs(self, args):
        #TODO
        return "324AF"

Srikanth Raju's avatar
Srikanth Raju committed
156 157
    def domac(self, args):
        ip = self.client_address[0]
158
        rows = DBQueryWarn("select mac from interfaces"
Srikanth Raju's avatar
Srikanth Raju committed
159
            " where interfaces.ip=%s",(ip,))
160 161
        if len(rows) > 0:
            mac = rows[0]
Srikanth Raju's avatar
Srikanth Raju committed
162 163 164 165 166 167 168 169
        else:
            return ""

        split = [mac[0][i:i+2] for i in range(0, len(mac[0]),2)]
        return ":".join(split)

    def doinstance_id(self, args):
        ip = self.client_address[0]
170
        rows = DBQueryWarn("select uuid from interfaces"
Srikanth Raju's avatar
Srikanth Raju committed
171
            " where interfaces.ip=%s",(ip,))
172 173
        if len(rows) > 0:
            uuid = rows[0]
Srikanth Raju's avatar
Srikanth Raju committed
174 175 176 177 178 179 180 181 182 183
        else:
            return ""

        return uuid[0]


    def dopublic_keys(self, args):
        if len(args) == 0:
            #Throw out all the users. Hope the stuff don't change between queries
            ip = self.client_address[0]
184
            rows = DBQueryWarn("(select user_pubkeys.uid,user_pubkeys.idx from user_pubkeys "
Srikanth Raju's avatar
Srikanth Raju committed
185 186 187 188
                "join group_membership on group_membership.uid = user_pubkeys.uid "
                "join experiments on experiments.pid=group_membership.pid AND experiments.gid=group_membership.gid "
                "join reserved on reserved.exptidx=experiments.idx "
                "join interfaces on reserved.node_id=interfaces.node_id "
189
                "where interfaces.ip=%s and user_pubkeys.uid=experiments.expt_swap_uid ORDER BY idx DESC) "
190
                "UNION "
191
                "(select user_pubkeys.uid,user_pubkeys.idx from user_pubkeys "
192 193 194 195
                "join group_membership on group_membership.uid = user_pubkeys.uid "
                "join experiments on experiments.pid=group_membership.pid AND experiments.gid=group_membership.gid "
                "join reserved on reserved.exptidx=experiments.idx "
                "join interfaces on reserved.node_id=interfaces.node_id "
196
                "where interfaces.ip=%s and user_pubkeys.uid!=experiments.expt_swap_uid);"
197
                , (ip,ip,))
Srikanth Raju's avatar
Srikanth Raju committed
198 199 200

            list = ""
            ctr = 0
201 202
            if len(rows) > 0:
                for (user,uid) in rows:
Srikanth Raju's avatar
Srikanth Raju committed
203 204 205 206 207 208 209 210 211 212
                    list = list + str(ctr) + "=" + str(user) + str(uid) + "\n"
                    ctr = ctr+1
            else:
                return ""

            return list
        elif len(args) == 1:
            #TODO: Verify ig idx is within limits
            return "openssh-key"
        elif len(args) == 2:
213 214 215 216
            val = args[0]
            # Always check args before using in query!
            if not re.match("^[\d]*$", val):
                return ""
Srikanth Raju's avatar
Srikanth Raju committed
217
            ip = self.client_address[0]
218
            rows = DBQueryWarn("select * from "
Srikanth Raju's avatar
Srikanth Raju committed
219
                "((select user_pubkeys.pubkey from user_pubkeys "
220 221 222 223
                "join group_membership on group_membership.uid = user_pubkeys.uid "
                "join experiments on experiments.pid=group_membership.pid AND experiments.gid=group_membership.gid "
                "join reserved on reserved.exptidx=experiments.idx "
                "join interfaces on reserved.node_id=interfaces.node_id "
224
                "where interfaces.ip=%s and user_pubkeys.uid=experiments.expt_swap_uid ORDER BY idx DESC) "
225 226
                "UNION "
                "(select user_pubkeys.pubkey from user_pubkeys "
Srikanth Raju's avatar
Srikanth Raju committed
227 228 229 230
                "join group_membership on group_membership.uid = user_pubkeys.uid "
                "join experiments on experiments.pid=group_membership.pid AND experiments.gid=group_membership.gid "
                "join reserved on reserved.exptidx=experiments.idx "
                "join interfaces on reserved.node_id=interfaces.node_id "
Srikanth Raju's avatar
Srikanth Raju committed
231 232
                "where interfaces.ip=%s and user_pubkeys.uid!=experiments.expt_swap_uid)) "
                "as T limit " + str(val) + ", 1;",
233
                (ip, ip,))
Srikanth Raju's avatar
Srikanth Raju committed
234

235 236
            if len(rows):
                key = rows[0]
Srikanth Raju's avatar
Srikanth Raju committed
237 238 239 240 241
            else:
                return ""

            return key[0]

Srikanth Raju's avatar
Srikanth Raju committed
242
    metas = {
243 244 245 246 247 248 249 250 251 252
        "meta-data" : {
            "placement" : {"availability-zone" : doavail},
            "ami-id": doamiid,
            "local-hostname" : dolocal_hostname,
            "public-hostname":dolocal_hostname,
            "network": {"interfaces": {"macs" : domacs}},
            "mac":domac,
            "instance-id":doinstance_id,
            "public-keys": dopublic_keys },
        "user-data" : do_userdata
Srikanth Raju's avatar
Srikanth Raju committed
253
    }
254
    pass
Srikanth Raju's avatar
Srikanth Raju committed
255

256 257 258 259 260 261
#
# Check for debug flag.
# 
if len(sys.argv) > 1 and sys.argv[1] == "-d":
    debug = 1
    pass
Srikanth Raju's avatar
Srikanth Raju committed
262

263 264 265 266 267 268 269 270 271 272
#
# Daemonize when not running in debug mode.
#
if not debug:
    #
    # Connect to syslog.
    #
    syslog.openlog("tmcd-meta", syslog.LOG_PID,
                   getattr(syslog, "LOG_" + string.upper(TBFACIL)))
    syslog.syslog(syslog.LOG_INFO, "EC2 Meta server starting up");
273 274 275 276 277 278 279

    #
    # Daemonize. We redirect our output into a log file cause I have no
    # idea what is going to use plain print. 
    # Lifted from xmlrpc/sslxmlrpc_server.py.in
    #
    try:
280
        fp = open(TBDIR + "/log/ec2meta.log", "a");
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
        sys.stdout = fp
        sys.stderr = fp
        sys.stdin.close();
        pass
    except:
        print "Could not open log file for append"
        sys.exit(1);
        pass

    pid = os.fork()
    if pid:
        os.system("echo " + str(pid) + " > /var/run/tmcd-meta.pid")
        sys.exit(0)
        pass
    os.setsid();
    pass

298 299 300
server = HTTPServer((socket.gethostbyname(socket.gethostname()), 8787),
                    Ec2MetaHandler)
server.serve_forever()