]> git.llucax.com Git - software/pymin.git/blob - services/dhcp/__init__.py
Add a decorator to mark which callables are exported by the dispatcher.
[software/pymin.git] / services / dhcp / __init__.py
1 # vim: set encoding=utf-8 et sw=4 sts=4 :
2
3 from mako.template import Template
4 from mako.runtime import Context
5 from os import path
6 try:
7     import cPickle as pickle
8 except ImportError:
9     import pickle
10 try:
11     from dispatcher import handler
12 except ImportError:
13     def handler(f): return f # NOP for testing
14
15 __ALL__ = ('DhcpHandler',)
16
17 pickle_ext = '.pkl'
18 pickle_vars = 'vars'
19 pickle_hosts = 'hosts'
20
21 config_filename = 'dhcpd.conf'
22
23 template_dir = path.join(path.dirname(__file__), 'templates')
24
25 class Host:
26     r"""Host(name, ip, mac) -> Host instance :: Class representing a host.
27
28     name - Host name, should be a fully qualified name, but no checks are done.
29     ip - IP assigned to the hostname.
30     mac - MAC address to associate to the hostname.
31     """
32
33     def __init__(self, name, ip, mac):
34         r"Initialize Host object, see class documentation for details."
35         self.name = name
36         self.ip = ip
37         self.mac = mac
38
39 class HostHandler:
40     r"""HostHandler(hosts) -> HostHandler instance :: Handle a list of hosts.
41
42     This class is a helper for DhcpHandler to do all the work related to hosts
43     administration.
44
45     hosts - A dictionary with string keys (hostnames) and Host instances values.
46     """
47
48     def __init__(self, hosts):
49         r"Initialize HostHandler object, see class documentation for details."
50         self.hosts = hosts
51
52     @handler
53     def add(self, name, ip, mac):
54         r"add(name, ip, mac) -> None :: Add a host to the hosts list."
55         # XXX deberia indexar por hostname o por ip? o por mac? :)
56         # o por nada... Puedo tener un nombre con muchas IPs? Una IP con muchos
57         # nombres? Una MAC con muchas IP? una MAC con muchos nombre? Etc...
58         self.hosts[name] = Host(name, ip, mac)
59
60     @handler
61     def update(self, name, ip=None, mac=None):
62         r"update(name[, ip[, mac]]) -> None :: Update a host of the hosts list."
63         if not name in self.hosts:
64             raise KeyError('Host not found')
65         if ip is not None:
66             self.hosts[name].ip = ip
67         if mac is not None:
68             self.hosts[name].mac = mac
69
70     @handler
71     def delete(self, name):
72         r"delete(name) -> None :: Delete a host of the hosts list."
73         if not name in self.hosts:
74             raise KeyError('Host not found')
75         del self.hosts[name]
76
77     @handler
78     def list(self):
79         r"""list() -> CSV string :: List all the hostnames.
80
81         The list is returned as a single CSV line with all the hostnames.
82         """
83         return ','.join(self.hosts)
84
85     @handler
86     def show(self):
87         r"""show() -> CSV string :: List all the complete hosts information.
88
89         The hosts are returned as a CSV list with each host in a line, like:
90         hostname,ip,mac
91         """
92         hosts = self.hosts.values()
93         return '\n'.join('%s,%s,%s' % (h.name, h.ip, h.mac) for h in hosts)
94
95 class DhcpHandler:
96     r"""DhcpHandler([pickle_dir[, config_dir]]) -> DhcpHandler instance.
97
98     Handles DHCP service commands for the dhcpd program.
99
100     pickle_dir - Directory where to write the persistent configuration data.
101
102     config_dir - Directory where to store de generated configuration files.
103
104     Both defaults to the current working directory.
105     """
106
107     def __init__(self, pickle_dir='.', config_dir='.'):
108         r"Initialize DhcpHandler object, see class documentation for details."
109         self.pickle_dir = pickle_dir
110         self.config_dir = config_dir
111         filename = path.join(template_dir, config_filename)
112         self.template = Template(filename=filename)
113         try:
114             self._load()
115         except IOError:
116             # This is the first time the handler is used, create a basic
117             # setup using some nice defaults
118             self.hosts = dict()
119             self.vars = dict(
120                 domain_name = 'example.com',
121                 dns_1       = 'ns1.example.com',
122                 dns_2       = 'ns2.example.com',
123                 net_address = '192.168.0.0',
124                 net_mask    = '255.255.255.0',
125                 net_start   = '192.168.0.100',
126                 net_end     = '192.168.0.200',
127                 net_gateway = '192.168.0.1',
128             )
129             self._dump()
130             self._write_config()
131         self.host = HostHandler(self.hosts)
132
133     @handler
134     def set(self, param, value):
135         r"set(param, value) -> None :: Set a DHCP parameter."
136         if not param in self.vars:
137             raise KeyError('Parameter ' + param + ' not found')
138         self.vars[param] = value
139
140     @handler
141     def list(self):
142         r"""list() -> CSV string :: List all the parameter names.
143
144         The list is returned as a single CSV line with all the names.
145         """
146         return ','.join(self.vars)
147
148     @handler
149     def show(self):
150         r"""show() -> CSV string :: List all the parameters (with their values).
151
152         The parameters are returned as a CSV list with each parameter in a
153         line, like:
154         name,value
155         """
156         return '\n'.join(('%s,%s' % (k, v) for (k, v) in self.vars.items()))
157
158     @handler
159     def start(self):
160         r"start() -> None :: Start the DHCP service."
161         #esto seria para poner en una interfaz
162         #y seria el hook para arrancar el servicio
163         pass
164
165     @handler
166     def stop(self):
167         r"stop() -> None :: Stop the DHCP service."
168         #esto seria para poner en una interfaz
169         #y seria el hook para arrancar el servicio
170         pass
171
172     @handler
173     def restart(self):
174         r"restart() -> None :: Restart the DHCP service."
175         #esto seria para poner en una interfaz
176         #y seria el hook para arrancar el servicio
177         pass
178
179     @handler
180     def reload(self):
181         r"reload() -> None :: Reload the configuration of the DHCP service."
182         #esto seria para poner en una interfaz
183         #y seria el hook para arrancar el servicio
184         pass
185
186     @handler
187     def commit(self):
188         r"commit() -> None :: Commit the changes and reload the DHCP service."
189         #esto seria para poner en una interfaz
190         #y seria que hace el pickle deberia llamarse
191         #al hacerse un commit
192         self._dump()
193         self._write_config()
194         self.reload()
195
196     @handler
197     def rollback(self):
198         r"rollback() -> None :: Discard the changes not yet commited."
199         self._load()
200
201     def _dump(self):
202         r"_dump() -> None :: Dump all persistent data to pickle files."
203         # XXX podría ir en una clase base
204         self._dump_var(self.vars, pickle_vars)
205         self._dump_var(self.hosts, pickle_hosts)
206
207     def _load(self):
208         r"_load() -> None :: Load all persistent data from pickle files."
209         # XXX podría ir en una clase base
210         self.vars = self._load_var(pickle_vars)
211         self.hosts = self._load_var(pickle_hosts)
212
213     def _pickle_filename(self, name):
214         r"_pickle_filename() -> string :: Construct a pickle filename."
215         # XXX podría ir en una clase base
216         return path.join(self.pickle_dir, name) + pickle_ext
217
218     def _dump_var(self, var, name):
219         r"_dump_var() -> None :: Dump a especific variable to a pickle file."
220         # XXX podría ir en una clase base
221         pickle.dump(var, file(self._pickle_filename(name), 'wb'), 2)
222
223     def _load_var(self, name):
224         r"_load_var() -> object :: Load a especific pickle file."
225         # XXX podría ir en una clase base
226         return pickle.load(file(self._pickle_filename(name)))
227
228     def _write_config(self):
229         r"_write_config() -> None :: Generate all the configuration files."
230         # XXX podría ir en una clase base, ver como generalizar variables a
231         # reemplazar en la template
232         out_file = file(path.join(self.config_dir, config_filename), 'w')
233         ctx = Context(out_file, hosts=self.hosts.values(), **self.vars)
234         self.template.render_context(ctx)
235
236 if __name__ == '__main__':
237
238     import os
239
240     dhcp_handler = DhcpHandler()
241
242     def dump():
243         print '-' * 80
244         print 'Variables:', dhcp_handler.list()
245         print dhcp_handler.show()
246         print
247         print 'Hosts:', dhcp_handler.host.list()
248         print dhcp_handler.host.show()
249         print '-' * 80
250
251     dump()
252
253     dhcp_handler.host.add('my_name','192.168.0.102','00:12:ff:56')
254
255     dhcp_handler.host.update('my_name','192.168.0.192','00:12:ff:56')
256
257     dhcp_handler.host.add('nico','192.168.0.188','00:00:00:00')
258
259     dhcp_handler.set('domain_name','baryon.com.ar')
260
261     try:
262         dhcp_handler.set('sarasa','baryon.com.ar')
263     except KeyError, e:
264         print 'Error:', e
265
266     dhcp_handler.commit()
267
268     dump()
269
270     for f in (pickle_vars + pickle_ext, pickle_hosts + pickle_ext,
271                                                             config_filename):
272         os.unlink(f)
273