Import first version of Ganeti Remote API
[ganeti-local] / daemons / ganeti-rapi
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21 """ Ganeti Remote API master script.
22 """
23
24 import glob
25 import logging
26 import optparse
27 import sys
28 import os
29
30 import ganeti.rapi.RESTHTTPServer
31
32 RELEASE_VERSION = 0.01
33 API_PORT = 5080
34
35
36 def ParseOptions():
37   """Parse the command line options.
38
39   Returns:
40     (options, args) as from OptionParser.parse_args()
41
42   """
43   parser = optparse.OptionParser(description="Ganeti Remote API",
44                     usage="%prog [-d] [-p port]",
45                     version="%%prog (ganeti) %s" % RELEASE_VERSION)
46
47   parser.add_option("-d", "--debug", dest="debug",
48                     help="Enable some debug messages",
49                     default=False, action="store_true")
50   parser.add_option("-p", "--port", dest="port",
51                     help="Port to run API",
52                     default=API_PORT)
53   parser.add_option("-S", "--https", dest="ssl",
54                     help="Secure HTTP protocol with SSL",
55                     default=False, action="store_true")
56   parser.add_option("-K", "--ssl-key", dest="ssl_key",
57                     help="SSL key",
58                     default=None, type="string")
59   parser.add_option("-C", "--ssl-cert", dest="ssl_cert",
60                     help="SSL certificate",
61                     default=None, type="string")
62   options, args = parser.parse_args()
63
64   if len(args) != 1 or args[0] not in ("start", "stop"):
65     print >>sys.stderr, "Usage: %s [-d] [-p port] start|stop\n" % sys.argv[0]
66     sys.exit(1)
67
68   if options.ssl:
69     if not (options.ssl_cert and options.ssl_key):
70       print >>sys.stderr, "For secure mode please provide " \
71                         "--ssl-key and --ssl-cert arguments"
72       sys.exit(1)
73
74   return options, args
75
76
77 def Port2PID(port):
78   """Map network port to PID.
79
80   Args:
81     port: A port number to map.
82
83   Return:
84     PID number.
85   """
86
87   _NET_STAT = ['/proc/net/tcp','/proc/net/udp']
88
89   inode2port = {}
90   port2pid = {}
91
92   for file in _NET_STAT:
93     try:
94       try:
95         f = open(file)
96         for line in f.readlines()[1:]:
97           d = line.split()
98           inode2port[long(d[9])] = int(d[1].split(':')[1], 16)
99       finally:
100         f.close()
101     except EnvironmentError:
102       # Nothing can be done
103       pass
104
105   fdlist = glob.glob('/proc/[0-9]*/fd/*')
106   for fd in fdlist:
107     try:
108       pid = int(fd.split('/')[2])
109       inode = long(os.stat(fd)[1])
110       if inode in inode2port:
111         port2pid[inode2port[inode]] = pid
112     except EnvironmentError:
113       # Nothing can be done
114       pass
115
116   if port in port2pid:
117     return port2pid[port]
118   else:
119     return None
120
121
122 def StartAPI(options):
123   """Start the API.
124
125   Args:
126     options: arguments.
127
128   Return:
129     Exit code.
130   """
131   port = int(options.port)
132   # do the UNIX double-fork magic
133   try:
134     pid = os.fork()
135     if pid > 0:
136       # exit first parent
137       sys.exit(0)
138   except OSError, e:
139     print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
140     return 1
141
142   # decouple from parent environment
143   os.chdir("/")
144   os.setsid()
145   os.umask(0)
146
147   # do second fork
148   try:
149     pid = os.fork()
150     if pid > 0:
151       # exit from second parent, print eventual PID before
152       print "Ganeti-RAPI PID: %d port: %d" % (pid, port)
153       return 0
154   except OSError, e:
155     print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
156     return 1
157
158   # start the daemon main loop
159   ganeti.rapi.RESTHTTPServer.start(options)
160
161
162 def StopAPI(options):
163   """Stop the API."""
164   port = int(options.port)
165   try:
166     pid = Port2PID(port)
167     if pid:
168       print "Stopping Ganeti-RAPI PID: %d, port: %d.... " % (pid, port),
169       os.kill(pid, 9)
170       print "done."
171     else:
172       print >>sys.stderr, "Unable to locate running Ganeti-RAPI on port: %d" % port
173   except Exception, ex:
174     print >>sys.stderr, ex
175     return 1
176   return 0
177
178
179 def main():
180   """Main function.
181
182   """
183   result = 1
184   options, args = ParseOptions()
185   if args[0] == "start":
186     result = StartAPI(options)
187   else:
188     result = StopAPI(options)
189   sys.exit(result)
190
191
192 if __name__ == '__main__':
193   main()