recipes to dump partition configuration or buildout section in json files
[slapos.git] / slapos / recipe / slapconfiguration.py
1 ##############################################################################
2 #
3 # Copyright (c) 2012 Vifib SARL and Contributors. All Rights Reserved.
4 #
5 # WARNING: This program as such is intended to be used by professional
6 # programmers who take the whole responsibility of assessing all potential
7 # consequences resulting from its eventual inadequacies and bugs
8 # End users who are looking for a ready-to-use solution with commercial
9 # guarantees and support are strongly adviced to contract a Free Software
10 # Service Company
11 #
12 # This program is Free Software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 3
15 # of the License, or (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #
26 ##############################################################################
27
28 import json
29 import os
30
31 import slapos.slap
32 from slapos.recipe.librecipe import unwrap
33 from ConfigParser import RawConfigParser
34 from netaddr import valid_ipv4, valid_ipv6
35
36 class Recipe(object):
37 """
38 Retrieves slap partition parameters, and makes them available to other
39 buildout section in various ways, and in various encodings.
40 Populates the buildout section it is used in with all slap partition
41 parameters.
42 Also provides access to partition properties: all IPv4, IPv6 and tap
43 interfaces it is allowed to use.
44
45 Input:
46 url
47 Slap server url.
48 Example:
49 ${slap-connection:server-url}
50 key & cert (optional)
51 Path of files containing key and certificate for secure connection to
52 slap server.
53 Example:
54 ${slap-connection:key-file}
55 ${slap-connection:cert-file}
56 computer
57 Computer identifier.
58 Example:
59 ${slap-connection:computer-id}
60 partition
61 Partition identifier.
62 Example:
63 ${slap-connection:partition-id}
64
65 Output:
66 slap-software-type
67 Current partition's software type.
68 ipv4
69 Set of IPv4 addresses.
70 ipv6
71 Set of IPv6 addresses.
72 ipv4-random
73 One of the IPv4 addresses.
74 ipv6-random
75 One of the IPv6 addresses.
76 tap
77 Set of TAP interfaces.
78 configuration
79 Dict of all parameters.
80 configuration.<key>
81 One key per partition parameter.
82 Partition parameter whose name cannot be represented unambiguously in
83 buildout syntax are ignored. They cannot be accessed from buildout syntax
84 anyway, and are available through "configuration" output key.
85 instance-state
86 The instance state.
87 """
88
89 # XXX: used to detect if a configuration key is a valid section key. This
90 # assumes buildout uses ConfigParser - which is currently the case.
91 OPTCRE_match = RawConfigParser.OPTCRE.match
92
93 def __init__(self, buildout, name, options):
94 parameter_dict = self.fetch_parameter_dict(options)
95
96 match = self.OPTCRE_match
97 for key, value in parameter_dict.iteritems():
98 if match(key) is not None:
99 continue
100 options['configuration.' + key] = value
101
102 def fetch_parameter_dict(self, options):
103 slap = slapos.slap.slap()
104 slap.initializeConnection(
105 options['url'],
106 options.get('key'),
107 options.get('cert'),
108 )
109 computer_partition = slap.registerComputerPartition(
110 options['computer'],
111 options['partition'],
112 )
113 parameter_dict = computer_partition.getInstanceParameterDict()
114 options['instance-state'] = computer_partition.getState()
115 # XXX: those are not partition parameters, strictly speaking.
116 # Make them available as individual section keys.
117 for his_key in (
118 'slap_software_type',
119 'slap_computer_partition_id',
120 'slap_computer_id',
121 'slap_software_release_url',
122 'slave_instance_list',
123 'timestamp',
124 ):
125 try:
126 value = parameter_dict.pop(his_key)
127 except KeyError:
128 pass
129 else:
130 options[his_key.replace('_', '-')] = value
131 ipv4_set = set()
132 v4_add = ipv4_set.add
133 ipv6_set = set()
134 v6_add = ipv6_set.add
135 tap_set = set()
136 tap_add = tap_set.add
137 for tap, ip in parameter_dict.pop('ip_list'):
138 tap_add(tap)
139 if valid_ipv4(ip):
140 v4_add(ip)
141 elif valid_ipv6(ip):
142 v6_add(ip)
143 # XXX: emit warning on unknown address type ?
144 options['ipv4'] = ipv4_set
145 options['ipv6'] = ipv6_set
146
147 # also export single ip values for those recipes that don't support sets.
148 if ipv4_set:
149 options['ipv4-random'] = list(ipv4_set)[0].encode('UTF-8')
150 if ipv6_set:
151 options['ipv6-random'] = list(ipv6_set)[0].encode('UTF-8')
152
153 options['tap'] = tap_set
154 return self._expandParameterDict(options, parameter_dict)
155
156 def _expandParameterDict(self, options, parameter_dict):
157 options['configuration'] = parameter_dict
158 return parameter_dict
159
160 install = update = lambda self: []
161
162 class Serialised(Recipe):
163 def _expandParameterDict(self, options, parameter_dict):
164 options['configuration'] = parameter_dict = unwrap(parameter_dict)
165 if isinstance(parameter_dict, dict):
166 return parameter_dict
167 else:
168 return {}
169
170 class JsonDump(Recipe):
171 def __init__(self, buildout, name, options):
172 parameter_dict = self.fetch_parameter_dict(options)
173 with os.fdopen(os.open(options['json-output'], os.O_WRONLY | os.O_CREAT, 0600), 'w') as fout:
174 fout.write(json.dumps(parameter_dict, indent=2, sort_keys=True))
175