slapconfiguration recipe: cast some parameters from unicode to str.
[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 import slapos.slap
28 from slapos.recipe.librecipe import unwrap
29 from ConfigParser import RawConfigParser
30 from netaddr import valid_ipv4, valid_ipv6
31
32 class Recipe(object):
33 """
34 Retrieves slap partition parameters, and makes them available to other
35 buildout section in various ways, and in various encodings.
36 Populates the buildout section it is used in with all slap partition
37 parameters.
38 Also provides access to partition properties: all IPv4, IPv6 and tap
39 interfaces it is allowed to use.
40
41 Input:
42 url
43 Slap server url.
44 Example:
45 ${slap-connection:server-url}
46 key & cert (optional)
47 Path of files containing key and certificate for secure connection to
48 slap server.
49 Example:
50 ${slap-connection:key-file}
51 ${slap-connection:cert-file}
52 computer
53 Computer identifier.
54 Example:
55 ${slap-connection:computer-id}
56 partition
57 Partition identifier.
58 Example:
59 ${slap-connection:partition-id}
60
61 Output:
62 slap-software-type
63 Current partition's software type.
64 ipv4
65 Set of IPv4 addresses.
66 ipv6
67 Set of IPv6 addresses.
68 ipv4-random
69 One of the IPv4 addresses.
70 ipv6-random
71 One of the IPv6 addresses.
72 tap
73 Set of TAP interfaces.
74 configuration
75 Dict of all parameters.
76 configuration.<key>
77 One key per partition parameter.
78 Partition parameter whose name cannot be represented unambiguously in
79 buildout syntax are ignored. They cannot be accessed from buildout syntax
80 anyway, and are available through "configuration" output key.
81 instance-state
82 The instance state.
83 """
84
85 # XXX: used to detect if a configuration key is a valid section key. This
86 # assumes buildout uses ConfigParser - which is currently the case.
87 OPTCRE_match = RawConfigParser.OPTCRE.match
88
89 def __init__(self, buildout, name, options):
90 slap = slapos.slap.slap()
91 slap.initializeConnection(
92 options['url'],
93 options.get('key'),
94 options.get('cert'),
95 )
96 computer_partition = slap.registerComputerPartition(
97 options['computer'],
98 options['partition'],
99 )
100 parameter_dict = computer_partition.getInstanceParameterDict()
101 options['instance-state'] = computer_partition.getState()
102 # XXX: those are not partition parameters, strictly speaking.
103 # Make them available as individual section keys.
104 for his_key in (
105 'slap_software_type',
106 'slap_computer_partition_id',
107 'slap_computer_id',
108 'slap_software_release_url',
109 'slave_instance_list',
110 'timestamp',
111 ):
112 try:
113 value = parameter_dict.pop(his_key)
114 except KeyError:
115 pass
116 else:
117 options[his_key.replace('_', '-')] = value
118 ipv4_set = set()
119 v4_add = ipv4_set.add
120 ipv6_set = set()
121 v6_add = ipv6_set.add
122 tap_set = set()
123 tap_add = tap_set.add
124 for tap, ip in parameter_dict.pop('ip_list'):
125 tap_add(tap)
126 if valid_ipv4(ip):
127 v4_add(ip)
128 elif valid_ipv6(ip):
129 v6_add(ip)
130 # XXX: emit warning on unknown address type ?
131 options['ipv4'] = ipv4_set
132 options['ipv6'] = ipv6_set
133
134 # also export single ip values for those recipes that don't support sets.
135 if ipv4_set:
136 options['ipv4-random'] = list(ipv4_set)[0].encode('UTF-8')
137 if ipv6_set:
138 options['ipv6-random'] = list(ipv6_set)[0].encode('UTF-8')
139
140 options['tap'] = tap_set.encode('UTF-8')
141 parameter_dict = self._expandParameterDict(options, parameter_dict)
142 match = self.OPTCRE_match
143 for key, value in parameter_dict.iteritems():
144 if match(key) is not None:
145 continue
146 options['configuration.' + key] = value
147
148 def _expandParameterDict(self, options, parameter_dict):
149 options['configuration'] = parameter_dict
150 return parameter_dict
151
152 install = update = lambda self: []
153
154 class Serialised(Recipe):
155 def _expandParameterDict(self, options, parameter_dict):
156 options['configuration'] = parameter_dict = unwrap(parameter_dict)
157 if isinstance(parameter_dict, dict):
158 return parameter_dict
159 else:
160 return {}