PROJECT_MOVED -> https://lab.nexedi.com/nexedi/slapos
[slapos.git] / slapos / recipe / softwaretype.py
1 ##############################################################################
2 #
3 # Copyright (c) 2010 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 os
29 import sys
30 import copy
31 from ConfigParser import ConfigParser
32 import json
33 import subprocess
34 import slapos.slap
35 import netaddr
36 import logging
37 import errno
38
39 import zc.buildout
40
41 class Recipe:
42
43 def __init__(self, buildout, name, options):
44 self.buildout = buildout
45 self.options = options
46 self.name = name
47 self.logger = logging.getLogger(self.name)
48
49 def _getIpAddress(self, test_method):
50 """Internal helper method to fetch ip address"""
51 if not 'ip_list' in self.parameter_dict:
52 raise AttributeError
53 for name, ip in self.parameter_dict['ip_list']:
54 if test_method(ip):
55 return ip
56 raise AttributeError
57
58 def _getTapIpAddressList(self, test_method):
59 """Internal helper method to fetch full ip address assigned for tap"""
60 if not 'full_ip_list' in self.parameter_dict:
61 return ()
62 for item in self.parameter_dict['full_ip_list']:
63 if len(item) == 5:
64 tap, ip, gw, mask, net = item
65 if tap.startswith('route_') and test_method(ip) and \
66 test_method(gw) and test_method(mask):
67 return (ip, gw, mask, net)
68 return ()
69
70 def getLocalIPv4Address(self):
71 """Returns local IPv4 address available on partition"""
72 # XXX: Lack checking for locality of address
73 return self._getIpAddress(netaddr.valid_ipv4)
74
75 def getGlobalIPv6Address(self):
76 """Returns global IPv6 address available on partition"""
77 # XXX: Lack checking for globality of address
78 return self._getIpAddress(netaddr.valid_ipv6)
79
80 def getLocalTapIPv4AddressList(self):
81 """Returns global IPv6 address available for tap interface"""
82 # XXX: Lack checking for locality of address
83 return self._getTapIpAddressList(netaddr.valid_ipv4)
84
85 def getNetworkInterface(self):
86 """Returns the network interface available on partition"""
87 if not 'ip_list' in self.parameter_dict:
88 raise AttributeError
89 for name, ip in self.parameter_dict['ip_list']:
90 if name:
91 return name
92 raise AttributeError, "Not network interface found"
93
94 def mkdir_p(self, path, mode=0700):
95 """
96 Creates a directory and its parents, if needed.
97 NB: If the directory already exists, it does not change its permission.
98 """
99
100 try:
101 os.makedirs(path, mode)
102 except OSError as exc:
103 if exc.errno == errno.EEXIST and os.path.isdir(path):
104 pass
105 else:
106 raise
107
108 def install(self):
109 slap = slapos.slap.slap()
110 slap_connection = self.buildout['slap_connection']
111 computer_id = slap_connection['computer_id']
112 computer_partition_id = slap_connection['partition_id']
113 server_url = slap_connection['server_url']
114 key_file = slap_connection.get('key_file')
115 cert_file = slap_connection.get('cert_file')
116 instance_root = self.buildout['buildout']['directory']
117 storage_configuration_dict = self.buildout.get('storage-configuration')
118 network_dict = self.buildout.get('network-information')
119 storage_home = ''
120 global_ipv4_network = ''
121 if storage_configuration_dict:
122 storage_home = storage_configuration_dict.get('storage-home')
123 if network_dict:
124 global_ipv4_network = network_dict.get('global-ipv4-network')
125 slap.initializeConnection(server_url, key_file, cert_file)
126 self.computer_partition = slap.registerComputerPartition(
127 computer_id,
128 computer_partition_id)
129 self.parameter_dict = self.computer_partition.getInstanceParameterDict()
130 software_type = self.parameter_dict['slap_software_type']
131
132 # Raise if request software_type does not exist ...
133 if software_type not in self.options:
134 # ... Except for backward compatibility. Then use "default".
135 if software_type in ['RootSoftwareInstance']:
136 software_type = 'default'
137 else:
138 raise zc.buildout.UserError("This software type (%s) isn't mapped." % \
139 software_type)
140
141 instance_file_path = self.options[software_type]
142
143 if not os.path.exists(instance_file_path):
144 raise zc.buildout.UserError("The specified buildout config file %r does "
145 "not exist." % instance_file_path)
146
147 buildout = ConfigParser()
148 with open(instance_file_path) as instance_path:
149 buildout.readfp(instance_path)
150
151 buildout.set('buildout', 'installed', '.installed-%s.cfg' % self.name)
152
153 if not buildout.has_section('slap-parameter'):
154 buildout.add_section('slap-parameter')
155 for parameter, value in self.parameter_dict.items():
156 # All parameters evaluating to False are... False, and shouldn't
157 # convey any information.
158 # Here, all those parameters are simply ignored.
159 if value:
160 if isinstance(value, str):
161 buildout.set('slap-parameter', parameter, value)
162 else:
163 buildout.set('slap-parameter', parameter, json.dumps(value))
164
165 buildout.add_section('slap-network-information')
166 buildout.set('slap-network-information', 'local-ipv4',
167 self.getLocalIPv4Address())
168 buildout.set('slap-network-information', 'global-ipv6',
169 self.getGlobalIPv6Address())
170 buildout.set('slap-network-information', 'network-interface',
171 self.getNetworkInterface())
172 tap_ip_list = self.getLocalTapIPv4AddressList()
173 tap_ipv4 = tap_gateway = tap_netmask = tap_network = ''
174 if tap_ip_list:
175 tap_ipv4, tap_gateway, tap_netmask, tap_network= tap_ip_list
176 buildout.set('slap-network-information', 'tap-ipv4', tap_ipv4)
177 buildout.set('slap-network-information', 'tap-gateway', tap_gateway)
178 buildout.set('slap-network-information', 'tap-netmask', tap_netmask)
179 buildout.set('slap-network-information', 'tap-network', tap_network)
180 buildout.set('slap-network-information', 'global-ipv4-network',
181 global_ipv4_network)
182
183 # Copy/paste slap_connection
184 buildout.add_section('slap-connection')
185 for key, value in self.buildout['slap_connection'].iteritems():
186 # XXX: Waiting for SlapBaseRecipe to use dash instead of underscores
187 buildout.set('slap-connection', key.replace('_', '-'), value)
188 # XXX: Needed for lxc. Use non standard API
189 buildout.set('slap-connection', 'requested', self.computer_partition._requested_state)
190
191 # setup storage directory
192 buildout.add_section('storage-configuration')
193 buildout.set('storage-configuration', 'storage-home', storage_home)
194 if storage_home and os.path.exists(storage_home) and \
195 os.path.isdir(storage_home):
196 # Create folder instance_root/DATA/ if not exist
197 data_home = os.path.join(instance_root, 'DATA')
198 self.mkdir_p(data_home)
199 for filename in os.listdir(storage_home):
200 storage_path = os.path.join(storage_home, filename, computer_partition_id)
201 if os.path.exists(storage_path) and os.path.isdir(storage_path):
202 storage_link = os.path.join(data_home, filename)
203 if os.path.lexists(storage_link):
204 if not os.path.islink(storage_link):
205 raise zc.buildout.UserError(
206 'Target %r already exists but is not a link' % storage_link)
207 #os.unlink(storage_link)
208 else:
209 os.symlink(storage_path, storage_link)
210 buildout.set('storage-configuration', filename, storage_link)
211
212 work_directory = os.path.abspath(self.buildout['buildout'][
213 'directory'])
214 buildout_filename = os.path.join(work_directory,
215 'buildout-%s.cfg' % self.name)
216 with open(buildout_filename, 'w') as buildout_file:
217 buildout.write(buildout_file)
218
219 # XXX-Antoine: We gotta find a better way to do this. I tried to check
220 # out how slapgrid-cp was running buildout. But it is worse than that.
221 command_line_args = copy.copy(sys.argv) + ['-c', buildout_filename]
222
223 self.logger.info("Invoking commandline : '%s'",
224 ' '.join(command_line_args))
225
226 subprocess.check_call(command_line_args, cwd=work_directory,
227 env=os.environ.copy())
228 return []
229 update = install