diff options
Diffstat (limited to 'meta-openstack/classes')
-rw-r--r-- | meta-openstack/classes/openstackchef.bbclass | 115 | ||||
-rw-r--r-- | meta-openstack/classes/openstackchef_inc.bbclass | 831 |
2 files changed, 946 insertions, 0 deletions
diff --git a/meta-openstack/classes/openstackchef.bbclass b/meta-openstack/classes/openstackchef.bbclass new file mode 100644 index 0000000..c2e4a40 --- /dev/null +++ b/meta-openstack/classes/openstackchef.bbclass | |||
@@ -0,0 +1,115 @@ | |||
1 | # openstackchef.bbclass | ||
2 | # Copyright (c) 2014 Wind River Systems, Inc. | ||
3 | # | ||
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
5 | # of this software and associated documentation files (the "Software"), to | ||
6 | # deal | ||
7 | # in the Software without restriction, including without limitation the rights | ||
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | # copies of the Software, and to permit persons to whom the Software is | ||
10 | # furnished to do so, subject to the following conditions: | ||
11 | # | ||
12 | # The above copyright notice and this permission notice shall be included in | ||
13 | # all copies or substantial portions of the Software. | ||
14 | # | ||
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | # FROM, | ||
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
22 | # THE SOFTWARE. | ||
23 | # | ||
24 | # | ||
25 | # This class provides a framework for openstack services like python-neutron | ||
26 | # or python-nova to register their configuration files so that they can be | ||
27 | # recreated at run-time by chef-solo. Inheriting | ||
28 | # this class involves exposing configuration files from which this class | ||
29 | # creates chef-solo templates. These template files are later used by chef-solo | ||
30 | # to recreate the configuration files. | ||
31 | # | ||
32 | # For the templates files to be created, the recipes are expected | ||
33 | # to define the following variables variables: | ||
34 | # | ||
35 | # SRCNAME: | ||
36 | # This is the name of the package, neutron for example for the package | ||
37 | # python-neutron. It's appended to the names of template files and also | ||
38 | # used in the creation of package based temporary files. A default value | ||
39 | # of ${BPN} is assigned to this variable when it's not defined by recipes | ||
40 | # inheriting class. | ||
41 | # | ||
42 | # CHEF_SERVICES_CONF_FILES | ||
43 | # | ||
44 | # This variable provides the list of configuration files | ||
45 | # exposed to this class by the recipes inheriting the class. | ||
46 | # These files are expected to be in the image( ${D}) directory, though ${D} | ||
47 | # directory is excluded from the file name. Definition of this variable | ||
48 | # by recipe files is optional. | ||
49 | # eg. | ||
50 | # CHEF_SERVICES_CONF_FILES="\ | ||
51 | # ${sysconfdir}/chef/neutron/plugins/linuxbridge/linuxbridge_conf.ini.rb \ | ||
52 | # ${sysconfdir}/chef/neutron/neutron.conf | ||
53 | # " | ||
54 | # | ||
55 | #INITSCRIPT_PACKAGES | ||
56 | #This variable provides a mechanism for recipes inheriting this class | ||
57 | #to provide a list of services to start/stop when their configuration files | ||
58 | #are recreated on disk. | ||
59 | #This variable is assumed to be provided by all recipes that register a set | ||
60 | #of configuration files with this class. Failing to do so will lead to | ||
61 | #service not reloading the newly created configuration file(s) at run-time. | ||
62 | # | ||
63 | # | ||
64 | #INITSCRIPT_NAME_x or INITSCRIPT_NAME | ||
65 | #This variable is also assumed to be set by recipes inheriting this class. | ||
66 | #It specifies the names of the services to start/stop as specified above. | ||
67 | #Like the variable immediately above, failure to provide this variable will | ||
68 | #lead to mis-configuration of the service at run-time. | ||
69 | # | ||
70 | # | ||
71 | #INITSCRIPT_PARAMS_x or INITSCRIPT_PARAMS | ||
72 | #Like the last two variable above, this variable is also assumed to be set | ||
73 | #by recipes inheriting this class. It is used to extract the run-level | ||
74 | #priority of the INITSCRIPT_NAME variable(s). Unlike, the previous two | ||
75 | #variables, a default run-level is assigned to the script when this variable | ||
76 | #defaults to the string 'default' | ||
77 | # | ||
78 | # | ||
79 | # CHEF_SERVICES_SPECIAL_FUNC | ||
80 | # This variable is optional, and is the name of a shell callback function. | ||
81 | # Unlike the placeholder/value substitution which this class does, | ||
82 | # there are times when recipes need to do more than a simple placeholder/ | ||
83 | # value substitution. This is made possible with the use of the callback | ||
84 | # function. | ||
85 | # The callback function should be defined by the recipe(s) inheriting | ||
86 | # this class. When this variable is defined, this class will call the | ||
87 | # callback function and pass it the name of the file to manipulate | ||
88 | # in the form of the variable CHEF_SERVICES_FILE_NAME. This variable | ||
89 | # is then accessed in the callback function in the recipe file as | ||
90 | # ${CHEF_SERVICES_FILE_NAME} | ||
91 | # | ||
92 | inherit hosts openstackchef_inc | ||
93 | |||
94 | #Call this function after the do_install function have executed in | ||
95 | #recipes inheriting this class, this ensures that we get configuration | ||
96 | #files that have been moved to the images directory | ||
97 | addtask deploychef_install before do_package after do_install | ||
98 | python do_deploychef_install() { | ||
99 | if deploychef_not_rootfs_class(d) and \ | ||
100 | deploychef_openstackchef_enabled(d): | ||
101 | #copy configuration files from packages inheriting class | ||
102 | template_files_tuple = deploychef_copy_conf_files(d) | ||
103 | #convert configuration files into templates for chefsolo | ||
104 | deploychef_make_templates( d, template_files_tuple) | ||
105 | #Generate a list of startup/shutdown services | ||
106 | deploychef_make_startup_shutdown_list(d) | ||
107 | } | ||
108 | |||
109 | #Use of ROOTFS_POSTPROCESS_COMMAND enables us to hook into the post | ||
110 | #rootfs creation process and influence the work of openstack_configure_hosts. | ||
111 | #However, to ensure that our function deploychef_rootfs_postprocess_commands | ||
112 | #is called after openstack_configure_hosts and not before it, | ||
113 | #we add it in front of our callback function here. | ||
114 | ROOTFS_POSTPROCESS_COMMAND += "openstack_configure_hosts ; deploychef_rootfs_postprocess_commands ; " | ||
115 | |||
diff --git a/meta-openstack/classes/openstackchef_inc.bbclass b/meta-openstack/classes/openstackchef_inc.bbclass new file mode 100644 index 0000000..7795082 --- /dev/null +++ b/meta-openstack/classes/openstackchef_inc.bbclass | |||
@@ -0,0 +1,831 @@ | |||
1 | # openstackchef_inc.bbclass | ||
2 | # Copyright (c) 2014 Wind River Systems, Inc. | ||
3 | # | ||
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
5 | # of this software and associated documentation files (the "Software"), to | ||
6 | # deal | ||
7 | # in the Software without restriction, including without limitation the rights | ||
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | # copies of the Software, and to permit persons to whom the Software is | ||
10 | # furnished to do so, subject to the following conditions: | ||
11 | # | ||
12 | # The above copyright notice and this permission notice shall be included in | ||
13 | # all copies or substantial portions of the Software. | ||
14 | # | ||
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONE INFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | # FROM, | ||
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
22 | # THE SOFTWARE. | ||
23 | # | ||
24 | # | ||
25 | #This class is a helper class for openstackchef.bbclass and should not be be | ||
26 | #inherited on it's own. It implements the functionalities used in | ||
27 | #openstackchef.bbclass | ||
28 | CHEFPN = "openstackchef" | ||
29 | #This variable is here to support legacy use of recipes that inherit this | ||
30 | #class. By default these recipes will be built without openstackchef support. | ||
31 | #Set this variable to 'yes' in your local.conf or similar to enable | ||
32 | #support for OPENSTACKCHEF. | ||
33 | OPENSTACKCHEF_ENABLED ?='' | ||
34 | #This variable is defined in most of the openstack services, however, | ||
35 | #it defaults to bare package name for packages that do not define it. | ||
36 | SRCNAME ?= "${BPN}" | ||
37 | #This is the base directory of where deploychef's templates files | ||
38 | #reside in the classes that inherit it. | ||
39 | CHEF_TEMPLATE_BASE="${D}${sysconfdir}/${CHEFPN}" | ||
40 | CHEF_PACKAGE_BASE="${WORKDIR}/package${sysconfdir}/${CHEFPN}" | ||
41 | CHEF_IMAGE_BASE="${D}${sysconfdir}" | ||
42 | CHEF_ROOTFS_BASE="${sysconfdir}/${CHEFPN}" | ||
43 | #These are the prefixs and suffixs to create chefsolo-like placeholders | ||
44 | ERB_PREFIX = "<%=node[:" | ||
45 | ERB_SUFFIX = "]%>" | ||
46 | #These prefix and suffix are used to create our default values | ||
47 | ERB_DEFAULT_PREFIX="default[" | ||
48 | ERB_DEFAULT_SUFFIX="]" | ||
49 | #Chefsolo template file extension | ||
50 | TEMPLATE_EXTENSION='.erb' | ||
51 | #Build deploychef package since this class has run-time and buil-time dependency | ||
52 | #on it | ||
53 | DEPENDS_${PN} += "deploychef" | ||
54 | RDEPENDS_${PN} += "deploychef" | ||
55 | CHEF_VERSION = '1' | ||
56 | #For classes that define a special substitution function, this variable is | ||
57 | #set by this class and specifies the file named passed back to these function | ||
58 | #for any special substitution. | ||
59 | CHEF_SERVICES_FILE_NAME ?='' | ||
60 | |||
61 | #These are hard coded in the recipes files where they are used. | ||
62 | ADMIN_TENANT_NAME ?= 'admin' | ||
63 | DEMO_USER ?= 'demo' | ||
64 | #This is dafault value used by update-rc.d script at runtime when | ||
65 | #build-time update-rd.d class specifies INITSCRIPT_PARAMS as 'defaults' | ||
66 | CHEF_INITSCRIPT_PARAMS='defaults 20 10' | ||
67 | |||
68 | def deploychef_services_default_sub_dict(d): | ||
69 | """Provides our placeholder/value substitution dictionary for global use | ||
70 | |||
71 | This function returns as dictionary containing the default substitution pattern | ||
72 | as follows: | ||
73 | :<key>: Name of placeholder variable | ||
74 | :<value>: A tuple consisting of the place holder variable followed | ||
75 | by its default value | ||
76 | """ | ||
77 | sub_dict={} | ||
78 | #neutron | ||
79 | sub_dict['SERVICE_TENANT_NAME'] = ('%SERVICE_TENANT_NAME%' , | ||
80 | '${SERVICE_TENANT_NAME}') | ||
81 | sub_dict['SERVICE_PASSWORD'] = ('%SERVICE_PASSWORD%' , '${SERVICE_PASSWORD}') | ||
82 | |||
83 | sub_dict['DB_USER'] = ('%DB_USER%', '${DB_USER}') | ||
84 | sub_dict['DB_PASSWORD'] = ('%DB_PASSWORD%' , '${DB_PASSWORD}') | ||
85 | sub_dict['CONTROLLER_IP'] = ('%CONTROLLER_IP%', '${CONTROLLER_IP}') | ||
86 | sub_dict['CONTROLLER_HOST'] = ('%CONTROLLER_HOST%', '${CONTROLLER_HOST}') | ||
87 | #nova | ||
88 | sub_dict['COMPUTE_IP'] = ('%COMPUTE_IP%', '${COMPUTE_IP}') | ||
89 | sub_dict['COMPUTE_HOST'] = ('%COMPUTE_HOST%', '${COMPUTE_HOST}') | ||
90 | sub_dict['LIBVIRT_IMAGES_TYPE'] = ('%LIBVIRT_IMAGES_TYPE%' , '${LIBVIRT_IMAGES_TYPE}') | ||
91 | sub_dict['OS_PASSWORD'] = ('%OS_PASSWORD%' , '${ADMIN_PASSWORD}') | ||
92 | sub_dict['SERVICE_TOKEN'] = ('%SERVICE_TOKEN%' , '${SERVICE_TOKEN}') | ||
93 | #swfit | ||
94 | sub_dict['ADMIN_TENANT_NAME'] = ('%ADMIN_TENANT_NAME%' , '${ADMIN_TENANT_NAME}') | ||
95 | sub_dict['ADMIN_USER'] = ('%ADMIN_USER%' , '${ADMIN_TENANT_NAME}') | ||
96 | sub_dict['ADMIN_PASSWORD'] = ('%ADMIN_PASSWORD%' , '${ADMIN_PASSWORD}') | ||
97 | sub_dict['DEMO_USER'] = ('%DEMO_USER%' , '${DEMO_USER}') | ||
98 | sub_dict['DEMO_PASSWORD'] = ('%DEMO_PASSWORD%' , '${ADMIN_PASSWORD}') | ||
99 | #ceilometer | ||
100 | sub_dict['CEILOMETER_SECRET'] = ('%CEILOMETER_SECRET%' , '${CEILOMETER_SECRET}') | ||
101 | |||
102 | #keystone | ||
103 | sub_dict['TOKEN_FORMAT'] = ('%TOKEN_FORMAT%' , '${TOKEN_FORMAT}') | ||
104 | sub_dict['METADATA_SHARED_SECRET'] = ('%METADATA_SHARED_SECRET%' , '${METADATA_SHARED_SECRET}') | ||
105 | #cinder | ||
106 | sub_dict['CINDER_BACKUP_BACKEND_DRIVER'] = ('%CINDER_BACKUP_BACKEND_DRIVER%' , '${CINDER_BACKUP_BACKEND_DRIVER}') | ||
107 | #cloud-init | ||
108 | sub_dict['MANAGE_HOSTS'] = ('%MANAGE_HOSTS%' , '${MANAGE_HOSTS}') | ||
109 | sub_dict['HOSTNAME'] = ('%HOSTNAME%' , '${MY_HOST}') | ||
110 | #postgresql | ||
111 | sub_dict['DB_DATADIR'] = ('%DB_DATADIR%' , '${DB_DATADIR}') | ||
112 | #glance | ||
113 | sub_dict['GLANCE_DEFAULT_STORE'] = ('%GLANCE_DEFAULT_STORE%' , '${GLANCE_DEFAULT_STORE}') | ||
114 | |||
115 | #barbican | ||
116 | sub_dict['BARBICAN_MAX_PACKET_SIZE'] = ('%BARBICAN_MAX_PACKET_SIZE%' , '${BARBICAN_MAX_PACKET_SIZE}') | ||
117 | #ceph | ||
118 | sub_dict['PUBLIC_IP'] = ('%PUBLIC_IP%' , '${CONTROLLER_IP}') | ||
119 | sub_dict['PRIVATE_IP'] = ('%PRIVATE_IP%' , '${MY_IP}') | ||
120 | sub_dict['PUBLIC_DOMAIN'] = ('%PUBLIC_DOMAIN%' , '${PUBLIC_DOMAIN}') | ||
121 | sub_dict['HOST_NAME'] = ('%HOST_NAME%' , '${MY_HOST}') | ||
122 | sub_dict['CEPH_BACKING_FILE_SIZE'] = ('%CEPH_BACKING_FILE_SIZE%' , '${CEPH_BACKING_FILE_SIZE}') | ||
123 | |||
124 | #Most of the services have SERVICE_USER define but the values are different | ||
125 | service_user = d.getVar('SRCNAME', True) | ||
126 | if service_user: | ||
127 | service_user = service_user.upper() + '_SERVICE_USER' | ||
128 | sub_dict[service_user] = ('%SERVICE_USER%' , '${SRCNAME}') | ||
129 | return sub_dict | ||
130 | |||
131 | #This variable is the default sets of substitutions common to most of the | ||
132 | #services in an openstack installation. It means this package is not completely | ||
133 | #agnostic but at the same time it gives us the added advantage of not repeating | ||
134 | #ourselves in the recipe/class files that inherits this class. | ||
135 | CHEF_SERVICES_DEFAULT_CONF_SUBS := "${@deploychef_services_default_sub_dict(d)}" | ||
136 | |||
137 | |||
138 | def deploychef_not_rootfs_class(d): | ||
139 | """Filter out rootfs related classes | ||
140 | |||
141 | This function is used to help us selectively executes the creation of template | ||
142 | files and daemon start/stop list. It allows us to executes certain functions | ||
143 | when the recipe inheriting this class is related to rootfs creation. | ||
144 | """ | ||
145 | pkg = d.getVar('PN', True) or d.getVar('BPN', True) | ||
146 | #Skip test if recipe/class calling this class is related to rootfs image | ||
147 | #creation. | ||
148 | if 'image-' in pkg: | ||
149 | return False | ||
150 | else: | ||
151 | return True | ||
152 | |||
153 | def deploychef_openstackchef_enabled(d): | ||
154 | """Find out if openstackchef class usage is enabled | ||
155 | |||
156 | The variable OPENSTACKCHEF_ENABLED is use to support legacy functionality | ||
157 | by recipes inheriting this class. Assinging it an empty string disables | ||
158 | the functionality of this class for that recipe file. | ||
159 | This function helps us detect when this variable is set to an empty string. | ||
160 | """ | ||
161 | chef = d.getVar('OPENSTACKCHEF_ENABLED', True) | ||
162 | #Skip test if recipe/class calling this class is related to rootfs image | ||
163 | #creation. | ||
164 | if chef != '' : | ||
165 | return True | ||
166 | else: | ||
167 | return False | ||
168 | |||
169 | def deploychef_make_startup_shutdown_list(d): | ||
170 | """Create list of services to start/stop and save them to file | ||
171 | |||
172 | This function uses the update-rc.d environment variables defined | ||
173 | in the recipes/classes inheriting this class to create the | ||
174 | startup and shutdown services list as defined in the file. | ||
175 | This is important because, when we are replacing the configuration | ||
176 | files for the services, the services needs to be shutdown and | ||
177 | restarted after their configuration files are edited. | ||
178 | Therefore, the following variables must be defined by classes inheriting | ||
179 | this class. | ||
180 | |||
181 | INITSCRIPT_PACKAGES: A list of init scripts | ||
182 | INITSCRIPT_PARAMS_x: The default start/stop priority for the above scripts | ||
183 | """ | ||
184 | |||
185 | import os | ||
186 | if d.getVar('INITSCRIPT_PACKAGES') or d.getVar('INITSCRIPT_NAME'): | ||
187 | #script_list = bb.data.getVar('INITSCRIPT_PACKAGES', d , 1) | ||
188 | script_list = d.getVar('INITSCRIPT_PACKAGES', True) or \ | ||
189 | d.getVar('INITSCRIPT_NAME', True) #Some package do not define INITSCRIPT_PACKAGES | ||
190 | msg="list of start/stop services: %s" % str(script_list) | ||
191 | bb.debug(2, msg) | ||
192 | base_dir = bb.data.getVar('CHEF_TEMPLATE_BASE', d, 1 ) | ||
193 | if not os.path.exists(base_dir): | ||
194 | os.mkdir(base_dir) | ||
195 | startup_file = os.path.join(base_dir, d.getVar('SRCNAME', True) +'-startup-list') | ||
196 | shutdown_file = os.path.join(base_dir, d.getVar('SRCNAME', True) +'-shutdown-list') | ||
197 | msg ="Startup and shutdown files %s %s saved to %s" % \ | ||
198 | ( startup_file, shutdown_file, base_dir) | ||
199 | bb.note(msg) | ||
200 | try: | ||
201 | hStartup = open(startup_file, 'w') | ||
202 | hShutdown = open(shutdown_file, 'w') | ||
203 | for script_name in script_list.split(): | ||
204 | #Retrieve the default params for update-rc.d for this script | ||
205 | #INITSCRIPT_PARAMS_nova-api="defaults 30 10" | ||
206 | init_script_name ="INITSCRIPT_NAME_%s" % script_name | ||
207 | init_script_name = d.getVar(init_script_name, True) or \ | ||
208 | d.getVar('INITSCRIPT_NAME', True) | ||
209 | |||
210 | script_params ="INITSCRIPT_PARAMS_%s" % script_name | ||
211 | script_params = d.getVar(script_params, True) or \ | ||
212 | d.getVar('INITSCRIPT_PARAMS', True) | ||
213 | #If only defaults is provided as parameter, then use our default priority | ||
214 | if not script_params or len(script_params.split()) < 3: | ||
215 | script_params = d.getVar('CHEF_INITSCRIPT_PARAMS', True) | ||
216 | if init_script_name: | ||
217 | #eg. start 20 5 3 2 . stop 80 0 1 6 . | ||
218 | startup_priority = shutdown_priority = '' | ||
219 | if script_params.find('stop') > 0: | ||
220 | start, stop = script_params.split('stop') | ||
221 | start = start.split() | ||
222 | stop = stop.split() | ||
223 | startup_priority = start[1] | ||
224 | shutdown_priority = stop[0] | ||
225 | elif script_params.find('stop') == 0: | ||
226 | start, stop = script_params.split('start') | ||
227 | start = start.split() | ||
228 | stop = stop.split() | ||
229 | startup_priority = start[0] | ||
230 | shutdown_priority = stop[1] | ||
231 | else: | ||
232 | #"defaults 20 10" | ||
233 | defaults = script_params.split() | ||
234 | startup_priority = defaults[1] | ||
235 | shutdown_priority = defaults[2] | ||
236 | #Arrange the script's startup/shutdown format as in rc.x | ||
237 | startup_string = "%s%s%s%s" % ('S', startup_priority, init_script_name, os.linesep) | ||
238 | shutdown_string = "%s%s%s%s" % ('K', shutdown_priority, init_script_name, os.linesep) | ||
239 | hStartup.write(startup_string) | ||
240 | hShutdown.write(shutdown_string) | ||
241 | msg = "%s %s registered for startup and shutdown in" % \ | ||
242 | (startup_string, shutdown_string) | ||
243 | bb.debug(2 , msg) | ||
244 | else: | ||
245 | msg = "The variables INITSCRIPT_PARAMS_%s or INITSCRIPT_PARAMS \ | ||
246 | are not set in %s, startup/shutdown list not created for %s: %s" \ | ||
247 | % (script_name, d.getVar('FILE', True), d.getVar('SRCNAME', True), script_params) | ||
248 | raise bb.build.FuncFailed(msg) | ||
249 | hStartup.close() | ||
250 | hShutdown.close() | ||
251 | except IOError, e: | ||
252 | bb.error("Error opening startup/shutdown files %s %s, %s %s" % \ | ||
253 | (startup_file, shutdown_file, d.getVar('FILE'), e)) | ||
254 | else: | ||
255 | msg = "The variable INITSCRIPT_PACKAGES is not set in %s, \ | ||
256 | startup/shutdown script will not be made for %s package" % \ | ||
257 | (d.getVar('FILE', True), d.getVar('SRCNAME', True)) | ||
258 | bb.build.FuncFailed(msg) | ||
259 | |||
260 | def deploychef_make_substitutions(d, sub_dict, attr_filename, sed_filename): | ||
261 | """Make placeholder/value substitution in a file | ||
262 | |||
263 | This function makes placeholder substitution into the file named | ||
264 | sed_filename and appends the default value for those substitution into | ||
265 | the file named attr_filename. | ||
266 | The substitution is based on sets of placeholder/value pair in the | ||
267 | dictionary called sub_dict. | ||
268 | :sub_dict: name, placeholder and value dictionary | ||
269 | :attr_filename: chef-solo attributes file name | ||
270 | :sed_filename: configuration or template file | ||
271 | """ | ||
272 | if attr_filename and sed_filename: | ||
273 | if type(sub_dict) is dict: | ||
274 | import os | ||
275 | import re | ||
276 | sHandle = open(sed_filename, 'r+') | ||
277 | lines_in_conf_file = sHandle.read() | ||
278 | #We only want to append to the list of defines needed by this class | ||
279 | hFile= open(attr_filename,'a+') | ||
280 | lines_in_file= hFile.read() | ||
281 | for key in sub_dict.keys(): | ||
282 | placeholder , replacement = sub_dict[key] | ||
283 | #Filter by placeholder so that our attribute file only include | ||
284 | #defines for class being built | ||
285 | if re.search(placeholder,lines_in_conf_file): | ||
286 | #Make the substitution to create a template file out of | ||
287 | #the configuration file or just replace placeholder in | ||
288 | #configuration file | ||
289 | if d.getVar('TEMPLATE_EXTENSION', True) in sed_filename: | ||
290 | #Format the default attributes as expected by chefsolo | ||
291 | #for template file. | ||
292 | attr_string = d.getVar('ERB_DEFAULT_PREFIX', True) | ||
293 | attr_string += r'"' + key | ||
294 | attr_string += r'"' + d.getVar('ERB_DEFAULT_SUFFIX', True) | ||
295 | attr_string +=' = ' + r'"' + replacement + r'"' | ||
296 | #Only write default values that do not yet exist in file | ||
297 | #if key not in lines_in_file: | ||
298 | if not re.search(key,lines_in_file): | ||
299 | hFile.write("%s%s" % (attr_string, os.linesep)) | ||
300 | #Replace the placeholders in the current file, with | ||
301 | #new_replacement | ||
302 | new_replacement = d.getVar('ERB_PREFIX') + key | ||
303 | new_replacement += d.getVar('ERB_SUFFIX') | ||
304 | lines_in_conf_file = re.sub(placeholder, new_replacement, lines_in_conf_file) | ||
305 | #Write template file to disk | ||
306 | sHandle.seek(0) | ||
307 | sHandle.truncate() | ||
308 | sHandle.write(lines_in_conf_file) | ||
309 | sHandle.close() | ||
310 | |||
311 | hFile.close() | ||
312 | if False: | ||
313 | msg = "Cannot read/write to attributes file %s as expected:%s"\ | ||
314 | % (attr_filename, bb.data.getVar('FILE', d)) | ||
315 | bb.build.FuncFailed(msg) | ||
316 | else: | ||
317 | msg = "The substitution dictionary variable sub_dict is not set %s as expected "\ | ||
318 | % bb.data.getVar('FILE') | ||
319 | bb.error(msg) | ||
320 | else: | ||
321 | msg = "Null file names passsed to function %s %s "\ | ||
322 | % (bb.data.getVar('FUNC',d), bb.data.getVar('FILE', d)) | ||
323 | bb.error(msg) | ||
324 | |||
325 | |||
326 | |||
327 | def deploychef_make_templates( d, conf_tuple=tuple()): | ||
328 | """Create a template file out of a configuration file | ||
329 | |||
330 | Using substitution placeholders and values in the substitution | ||
331 | dictionary declared as CHEF_SERVICES_DEFAULT_CONF_SUBS, this function | ||
332 | makes the substitution for all placeholders. If the file is a ruby template file, | ||
333 | it replaces the placeholders with a chefsolo expression; | ||
334 | thereby creating a chefsolo template file. | ||
335 | |||
336 | :conf_tuple: List of configuration files | ||
337 | """ | ||
338 | |||
339 | if len(conf_tuple): | ||
340 | import os, ast | ||
341 | base_dir = bb.data.getVar('CHEF_TEMPLATE_BASE', d, 1 ) | ||
342 | attr_file = os.path.join(base_dir, d.getVar('SRCNAME', True) + '-attributes.rb') | ||
343 | msg ="Default attributes saved to %s" % attr_file | ||
344 | if os.path.exists(attr_file): | ||
345 | os.remove(attr_file) | ||
346 | bb.note(msg) | ||
347 | try: | ||
348 | for file_name in conf_tuple: | ||
349 | #If a special substitution function is defined for class | ||
350 | #inheriting this class, set the file name expected by special | ||
351 | #function before calling the function | ||
352 | special_func_name = d.getVar('CHEF_SERVICES_SPECIAL_FUNC') | ||
353 | if special_func_name: | ||
354 | bb.data.setVar('CHEF_SERVICES_FILE_NAME', file_name,\ | ||
355 | d) | ||
356 | bb.build.exec_func(special_func_name, d) | ||
357 | |||
358 | #Make the necessary susbstitutions using the default | ||
359 | #substitutiin dictionary | ||
360 | sub_dict = bb.data.getVar('CHEF_SERVICES_DEFAULT_CONF_SUBS', d , 1) | ||
361 | msg = "The variable %s is not set in %s as a dictionary as expected "\ | ||
362 | % ('CHEF_SERVICES_DEFAULT_CONF_SUBS', bb.data.getVar('FILE', d)) | ||
363 | if sub_dict: | ||
364 | #Safely retrieve our python data structure | ||
365 | sub_dict = ast.literal_eval(sub_dict) | ||
366 | if type(sub_dict) is dict: | ||
367 | deploychef_make_substitutions(d, sub_dict, attr_file, file_name) | ||
368 | else: | ||
369 | raise bb.build.FuncFailed(msg) | ||
370 | else: | ||
371 | raise bb.build.FuncFailed(msg) | ||
372 | #Make the necessary susbstitutions using auxilliary dictionary | ||
373 | #if provided by inheriting class | ||
374 | sub_dict = bb.data.getVar('CHEF_SERVICES_CONF_SUBS', d , 1) | ||
375 | if sub_dict: | ||
376 | sub_dict = ast.literal_eval(sub_dict) | ||
377 | msg = "The variable %s is not set in %s as a dictionary as expected "\ | ||
378 | % ('CHEF_SERVICES_CONF_SUB', bb.data.getVar('FILE', d)) | ||
379 | if type(sub_dict) is dict: | ||
380 | pass | ||
381 | deploychef_make_substitutions(d, sub_dict, attr_file, file_name) | ||
382 | else: | ||
383 | bb.build.FuncFailed(msg) | ||
384 | except IOError, e: | ||
385 | bb.error("Could not write to attribute file %s: in %s, %s" % \ | ||
386 | (attr_file, d.getVar('FILE'), e)) | ||
387 | |||
388 | def deploychef_copy_single_conf_file(d, dst_base, conf_file): | ||
389 | """Create a chef-solo template from an openstack configuration file | ||
390 | |||
391 | This function copies a single configuration file (conf_file) | ||
392 | to destination directory (dst_dir) and return a tuple that contains | ||
393 | both the absolute path of the conf_file and the template files it was | ||
394 | copied as. | ||
395 | """ | ||
396 | if conf_file: | ||
397 | import shutil | ||
398 | import os | ||
399 | #Create the absolute path to configuration file since it's with relative | ||
400 | #to image directory | ||
401 | image_base = d.getVar('D', True) | ||
402 | if conf_file.startswith(os.sep): | ||
403 | conf_file=conf_file[1:] | ||
404 | abs_conf_path = os.path.join(image_base, conf_file) | ||
405 | |||
406 | if os.path.exists(abs_conf_path): | ||
407 | dst_base = os.path.join(dst_base, os.path.dirname(conf_file)) | ||
408 | #make room for the template file about to be created | ||
409 | if not os.path.exists(dst_base): | ||
410 | os.makedirs(dst_base) | ||
411 | |||
412 | abs_template_file = os.path.basename(conf_file) + \ | ||
413 | d.getVar('TEMPLATE_EXTENSION', True) + '.' + d.getVar('SRCNAME', True) | ||
414 | abs_template_file = os.path.join(dst_base, abs_template_file) | ||
415 | #Copy conf file as template file | ||
416 | shutil.copy(abs_conf_path, abs_template_file) | ||
417 | msg = "\nConf file: %s\n Copied to: %s \n"\ | ||
418 | % (abs_conf_path, abs_template_file) | ||
419 | bb.debug(2, msg) | ||
420 | return (abs_conf_path, abs_template_file) | ||
421 | else: | ||
422 | msg = "Configuration file: %s in %s does not \ | ||
423 | exist" % (abs_conf_path, d.getVar('FILE')) | ||
424 | raise bb.build.FuncFailed(msg) | ||
425 | else: | ||
426 | msg = "The specified configuration file destined for %s in %s is an empty string\n" \ | ||
427 | % (dst_base, d.getVar('FILE')) | ||
428 | raise bb.build.FuncFailed(msg) | ||
429 | |||
430 | |||
431 | |||
432 | def deploychef_copy_conf_files(d): | ||
433 | """Copy openstack services' configuration files to be used as chef-solo templates | ||
434 | |||
435 | Copy the configuration file(s) for the services under | ||
436 | ${D}${sysconfdir}/${CHEFPN}/<conf_file>. | ||
437 | The file(s) is/are assumed to be located in the images directory; ${D}<conf_file> | ||
438 | And evaluate all necessary substitution in the configuration file. | ||
439 | """ | ||
440 | abs_template_list = list() | ||
441 | abs_conf_list = list() | ||
442 | |||
443 | #Retrieve our string of configuration files | ||
444 | conf_files = d.getVar('CHEF_SERVICES_CONF_FILES', True ) | ||
445 | #The template files that will be made from the configuration files will be | ||
446 | #copied with reference to this base directory. | ||
447 | dst_base = d.getVar('CHEF_TEMPLATE_BASE', True ) | ||
448 | if conf_files and len(conf_files.strip()): | ||
449 | conf_files = conf_files.split() | ||
450 | if len(conf_files) != 1: | ||
451 | for conf_file in conf_files: | ||
452 | abs_conf_path, abs_template_path = deploychef_copy_single_conf_file(d, \ | ||
453 | dst_base, conf_file) | ||
454 | if abs_template_path: | ||
455 | #Save the absolute path to the template file | ||
456 | abs_template_list.append(abs_template_path) | ||
457 | if abs_conf_path: | ||
458 | #Save the absolute path to the configuration file | ||
459 | abs_conf_list.append(abs_conf_path) | ||
460 | else: | ||
461 | abs_conf_path, abs_template_path = deploychef_copy_single_conf_file(d,\ | ||
462 | dst_base, conf_files[0]) | ||
463 | if abs_template_path: | ||
464 | #Save the absolute path to the template file | ||
465 | abs_template_list.append(abs_template_path) | ||
466 | if abs_conf_path: | ||
467 | #Save the absolute path to the template file | ||
468 | abs_conf_list.append(abs_conf_path) | ||
469 | #Since the recipes no longer do the substitution in the | ||
470 | #configuration files, let us do it for the configuration files | ||
471 | deploychef_make_templates(d, tuple(abs_conf_list)) | ||
472 | else: | ||
473 | msg = "The variable CHEF_SERVICES_CONF_FILES is not set" | ||
474 | msg += " in %s as a list of files as expected" % d.getVar('FILE', True) | ||
475 | #raise bb.build.FuncFailed(msg) | ||
476 | #No longer a requirement that all recipes inheriting this | ||
477 | #class must have a set of configuration files. | ||
478 | bb.debug(2,msg) | ||
479 | return tuple(abs_template_list) | ||
480 | |||
481 | def deploychef_postinst_substitutions(d, sub_dict, postinst): | ||
482 | """Make value substitution in openstack services' postinstall scripts | ||
483 | |||
484 | This function makes all necessary substitution in the 'setup' related postinsts | ||
485 | functions pgk_postinst_${PN}-setup. The substitution is base on entries in a | ||
486 | dictionary sub_dict. In addition it also updates the list of defined constansts | ||
487 | based on the values specified in dictionary or as specified by the recipe's | ||
488 | callback function. | ||
489 | |||
490 | :sub_dict: name, placeholder and value substitution dictionary | ||
491 | :postinst: content of an openstack service's postinstall script | ||
492 | |||
493 | """ | ||
494 | if postinst: | ||
495 | if type(sub_dict) is dict: | ||
496 | import re | ||
497 | base_dir = d.getVar('CHEF_PACKAGE_BASE', True) | ||
498 | attr_filename = os.path.join(base_dir, d.getVar('SRCNAME', True) + '-attributes.rb') | ||
499 | if os.path.exists(attr_filename): | ||
500 | hFile= open(attr_filename,'a+') | ||
501 | lines_in_file= hFile.read() | ||
502 | for key in sub_dict.keys(): | ||
503 | placeholder , replacement = sub_dict[key] | ||
504 | if replacement and ( re.search(placeholder, postinst) or \ | ||
505 | re.search(replacement, postinst)): | ||
506 | #If there is any remaining placeholder in the current string | ||
507 | #replace it. | ||
508 | new_replacement = d.getVar('ERB_PREFIX') + key | ||
509 | new_replacement += d.getVar('ERB_SUFFIX') | ||
510 | |||
511 | updated_postinst = re.sub(placeholder, new_replacement, postinst) | ||
512 | #If the placeholder has been substituted, look for the | ||
513 | #substitution and replace it with our template value | ||
514 | updated_postinst = re.sub(replacement, new_replacement, updated_postinst) | ||
515 | #Update our attributes file with the updated replacement | ||
516 | #string | ||
517 | attr_string = d.getVar('ERB_DEFAULT_PREFIX', True) | ||
518 | attr_string += r'"' + key | ||
519 | attr_string += r'"' + d.getVar('ERB_DEFAULT_SUFFIX', True) | ||
520 | attr_string +=' = ' + r'"' + replacement + r'"' | ||
521 | #Only write default values that do not yet exist in file | ||
522 | #if key not in lines_in_file: | ||
523 | if not re.search(key,lines_in_file): | ||
524 | hFile.write("%s%s" % (attr_string, os.linesep)) | ||
525 | |||
526 | postinst_msg= "placeholder %s \n replacement %s \n updated_postinst :\n %s \n" % \ | ||
527 | (placeholder, replacement, updated_postinst) | ||
528 | bb.debug(2, postinst_msg) | ||
529 | postinst = updated_postinst | ||
530 | hFile.close() | ||
531 | else: | ||
532 | msg = "The substitution dictionary variable sub_dict is not set %s as expected "\ | ||
533 | % bb.data.getVar('FILE') | ||
534 | bb.build.FuncFailed(msg) | ||
535 | else: | ||
536 | msg = "Null string passsed to function %s %s "\ | ||
537 | % (bb.data.getVar('FUNC',d), bb.data.getVar('FILE', d)) | ||
538 | bb.build.FuncFailed(msg) | ||
539 | return postinst | ||
540 | |||
541 | def deploychef_update_package_postinsts(d): | ||
542 | """Make placeholder/value substitution in openstack postinstall scripts | ||
543 | |||
544 | This function searches all the 'setup' related post-install scripts for | ||
545 | references to placeholders of interest; such as %CONTROLLER_IP%. | ||
546 | It replaces any such reference when it does find one with a placeholder | ||
547 | (<%=node[:CONTROLLER_IP]%>); that essentially converts the post-install | ||
548 | script to a chefsolo template. | ||
549 | """ | ||
550 | def update_postinst_package(pkg): | ||
551 | bb.debug(1, 'Updating placeholders in postinst for pkg_postinst_%s scripts' % pkg) | ||
552 | |||
553 | ldata = bb.data.createCopy(d) | ||
554 | overrides = ldata.getVar("OVERRIDES", True) | ||
555 | |||
556 | msg = "%s The override variable is %s" % (pkg, overrides) | ||
557 | bb.note(msg) | ||
558 | ldata.setVar("OVERRIDES", "%s:%s" % (pkg, overrides)) | ||
559 | |||
560 | bb.data.update_data(ldata) | ||
561 | postinst = ldata.getVar('pkg_postinst', True) | ||
562 | if postinst: | ||
563 | #Make the necessary substitutions using the default | ||
564 | #substitution dictionary | ||
565 | overrides = d.getVar("OVERRIDES", True) | ||
566 | msg = "%s The override variable is %s :\n %s " % (pkg, overrides, postinst) | ||
567 | bb.note(msg) | ||
568 | sub_dict = bb.data.getVar('CHEF_SERVICES_DEFAULT_CONF_SUBS', d , 1) | ||
569 | msg = "The variable %s is not set in %s as a dictionary as expected "\ | ||
570 | % ('CHEF_SERVICES_DEFAULT_CONF_SUBS', bb.data.getVar('FILE', d)) | ||
571 | if sub_dict: | ||
572 | import ast | ||
573 | #Safely retrieve our python data structure | ||
574 | sub_dict = ast.literal_eval(sub_dict) | ||
575 | if type(sub_dict) is dict: | ||
576 | import re | ||
577 | updated_postinst = deploychef_postinst_substitutions(d, sub_dict, postinst) | ||
578 | #Replace the placeholders in postinst script if any | ||
579 | d.setVar('pkg_postinst_%s' % pkg, updated_postinst) | ||
580 | else: | ||
581 | raise bb.build.FuncFailed(msg) | ||
582 | else: | ||
583 | raise bb.build.FuncFailed(msg) | ||
584 | else: | ||
585 | msg= "pkg_postinst_%s does not exist for %s\n" % (pkg, str(ldata)) | ||
586 | bb.note(msg) | ||
587 | bb.build.FuncFailed(msg) | ||
588 | |||
589 | packages = (d.getVar('PACKAGES', True) or "").split() | ||
590 | if packages != []: | ||
591 | for pkg in packages: | ||
592 | if pkg.endswith('setup'): | ||
593 | update_postinst_package(pkg) | ||
594 | |||
595 | python populate_packages_append() { | ||
596 | |||
597 | deploychef_update_package_postinsts(d) | ||
598 | } | ||
599 | |||
600 | def deploychef_add_file_to_FILES_PN(d, conf_file=None): | ||
601 | """Add all directories under a file name to FILES_${PN} variable | ||
602 | |||
603 | This function appends the name of the template file to the FILES_${PN}/${BPN} | ||
604 | bitbake variable to avoid QA warning about files built but not | ||
605 | added to rootfs. $CHEF_TEMPLATE_BASE/conf_file. Note that conf_file | ||
606 | is relative to the root filesystem as in /etc/neutron/neutron.conf | ||
607 | The template file will be located in /etc/${CHEFPN}/etc/neutron/neutron.conf.rb | ||
608 | Thefore, we need to make sure that all directories above the | ||
609 | template file are added to FILES_${PN} variable. | ||
610 | |||
611 | :conf_file: a chef-solo template file | ||
612 | """ | ||
613 | import re | ||
614 | import os | ||
615 | #Perform an override so that we can update FILES_${PN} variables | ||
616 | ldata = bb.data.createCopy(d) | ||
617 | overrides = ldata.getVar("OVERRIDES", True) | ||
618 | pkg = d.getVar('PN', True) or d.getVar('BPN', True) | ||
619 | files = d.getVar('FILES_%s' % pkg, True) | ||
620 | pkg_files = "FILES_%s" % pkg | ||
621 | ldata.setVar("OVERRIDES", "%s:%s" % (pkg_files, overrides)) | ||
622 | bb.data.update_data(ldata) | ||
623 | |||
624 | dest_base = d.getVar('CHEF_TEMPLATE_BASE') | ||
625 | pkg_imagedir = d.getVar('CHEF_ROOTFS_BASE', True) | ||
626 | #Add the packages image base directory if it does not already exist | ||
627 | if re.search(pkg_imagedir, files) == None: | ||
628 | #All the directory and all files in it | ||
629 | files = "%s %s" % ( files, pkg_imagedir) | ||
630 | files = "%s %s%s*" % ( files, pkg_imagedir, os.sep ) | ||
631 | d.setVar('FILES_%s' % pkg, files) | ||
632 | msg= "Updated FILES_%s: %s for base images dir" % (pkg, d.getVar('FILES_%s' % pkg, files)) | ||
633 | bb.debug(2,msg) | ||
634 | #All the files and all sub directory leading up to the package image base directory | ||
635 | if conf_file: | ||
636 | rel_basedir = os.path.dirname(conf_file) | ||
637 | if rel_basedir.startswith(os.sep): | ||
638 | rel_basedir = rel_basedir[1:] | ||
639 | rel_basedir = os.path.join(pkg_imagedir, rel_basedir) | ||
640 | if re.search(rel_basedir, files) == None: | ||
641 | files = "%s %s" % ( files, rel_basedir) | ||
642 | files = "%s %s%s*" % ( files, rel_basedir, os.sep ) | ||
643 | while rel_basedir.count(os.sep) > 4: | ||
644 | #Must be above /etc/chef/etc/ | ||
645 | rel_basedir_list = rel_basedir.split(os.sep) | ||
646 | rel_basedir = os.sep.join(rel_basedir_list[:-1]) | ||
647 | if re.search(rel_basedir, files) == None: | ||
648 | #All the directory and files in it | ||
649 | files = "%s %s" % ( files, rel_basedir) | ||
650 | files = "%s %s%s*" % ( files, rel_basedir, os.sep ) | ||
651 | bb.note(files) | ||
652 | bb.debug(2, files) | ||
653 | d.setVar('FILES_%s' % pkg, files) | ||
654 | msg= "Updated FILES_%s: %s " % (pkg, d.getVar('FILES_%s' % pkg, files)) | ||
655 | bb.debug(2,msg) | ||
656 | |||
657 | def deploychef_update_FILES_PN_variable(d): | ||
658 | """Indicate that the created chef-solo templates should be packaged | ||
659 | |||
660 | This function ensures that all the templates files which are based off | ||
661 | of configuration files exposed to this class are packaged up when they | ||
662 | are copied from the images directory to the various packages folders | ||
663 | This avoids the QA warning such as: | ||
664 | WARNING: For recipe python-neutron, the following files/directories were installed | ||
665 | but not shipped in any package: | ||
666 | """ | ||
667 | conf_files = d.getVar('CHEF_SERVICES_CONF_FILES', True ) | ||
668 | if conf_files and len(conf_files.strip()): | ||
669 | import shutil | ||
670 | import os | ||
671 | for conf_file in conf_files.split(): | ||
672 | deploychef_add_file_to_FILES_PN(d, conf_file) | ||
673 | else: | ||
674 | #Add the directory containing the start/stop scripts | ||
675 | deploychef_add_file_to_FILES_PN(d) | ||
676 | |||
677 | |||
678 | python populate_packages_prepend() { | ||
679 | |||
680 | deploychef_update_FILES_PN_variable(d) | ||
681 | } | ||
682 | |||
683 | #The sets of functions below are for post rootfs processing. Preparing files | ||
684 | #for chefsolo is a two stage process. First we must create the required files | ||
685 | #in the package's image directory; and this is mostly done by the functions | ||
686 | #above. | ||
687 | #And then we aggregate the files from their respective package directories | ||
688 | #and put them together for the deploychef package in the expected | ||
689 | #location. | ||
690 | CHEF_ROOT_DIR="${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}" | ||
691 | CHEF_CONF_DIR="${CHEF_ROOT_DIR}/${sysconfdir}" | ||
692 | INITD_DIR="${IMAGE_ROOTFS}/${sysconfdir}/init.d" | ||
693 | POSTINSTS_DIR="${IMAGE_ROOTFS}/${sysconfdir}/rpm-postinsts" | ||
694 | DEPLOYCHEF_DIR="${IMAGE_ROOTFS}/opt/deploychef" | ||
695 | DEPLOYCHEF_TEMPLATES_DIR="${DEPLOYCHEF_DIR}/cookbooks/openstack/templates/default" | ||
696 | ATTRIBUTES_DIR="${DEPLOYCHEF_DIR}/cookbooks/openstack/attributes" | ||
697 | ATTRIBUTES_FILE="${ATTRIBUTES_DIR}/default.rb" | ||
698 | |||
699 | deploychef_copy_host_files() { | ||
700 | #The /etc/hosts & /etc/hostname files are written during the rootfs | ||
701 | #post process, therefore the only way of making templates out of them | ||
702 | #is to hook into the rootfs post process command. | ||
703 | if [ -f "${IMAGE_ROOTFS}/${sysconfdir}/hosts" ]; then | ||
704 | #Convert etc/hosts to chefsolo template | ||
705 | cp ${IMAGE_ROOTFS}/${sysconfdir}/hosts ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/${sysconfdir}/hosts.erb | ||
706 | sed -e "s,${CONTROLLER_IP},${ERB_PREFIX}CONTROLLER_IP${ERB_SUFFIX},g" -i \ | ||
707 | ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/${sysconfdir}/hosts.erb | ||
708 | sed -e "s,${CONTROLLER_HOST},${ERB_PREFIX}CONTROLLER_HOST${ERB_SUFFIX},g" -i \ | ||
709 | ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/${sysconfdir}/hosts.erb | ||
710 | |||
711 | sed -e "s,${COMPUTE_IP},${ERB_PREFIX}COMPUTE_IP${ERB_SUFFIX},g" -i \ | ||
712 | ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/${sysconfdir}/hosts.erb | ||
713 | sed -e "s,${COMPUTE_HOST},${ERB_PREFIX}COMPUTE_HOST${ERB_SUFFIX},g" -i \ | ||
714 | ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/${sysconfdir}/hosts.erb | ||
715 | #Create an attribute file for /etc/hosts | ||
716 | attr_string="${ERB_DEFAULT_PREFIX}\"COMPUTE_IP\"${ERB_DEFAULT_SUFFIX} = \"${COMPUTE_IP}\"" | ||
717 | echo "$attr_string" > ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/hosts-attributes.rb | ||
718 | attr_string="${ERB_DEFAULT_PREFIX}\"COMPUTE_HOST\"${ERB_DEFAULT_SUFFIX} = \"${COMPUTE_HOST}\"" | ||
719 | echo "$attr_string" >> ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/hosts-attributes.rb | ||
720 | attr_string="${ERB_DEFAULT_PREFIX}\"CONTROLLER_IP\"${ERB_DEFAULT_SUFFIX} = \"${CONTROLLER_IP}\"" | ||
721 | echo "$attr_string" >> ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/hosts-attributes.rb | ||
722 | attr_string="${ERB_DEFAULT_PREFIX}\"CONTROLLER_HOST\"${ERB_DEFAULT_SUFFIX} = \"${CONTROLLER_HOST}\"" | ||
723 | echo "$attr_string" >> ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/hosts-attributes.rb | ||
724 | fi | ||
725 | |||
726 | if [ -f "${IMAGE_ROOTFS}/${sysconfdir}/hostname" ]; then | ||
727 | #Convert etc/hostname to chefsolo template | ||
728 | cp ${IMAGE_ROOTFS}/${sysconfdir}/hostname ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/${sysconfdir}/hostname.erb | ||
729 | sed -e "s,${MY_HOST},${ERB_PREFIX}HOSTNAME${ERB_SUFFIX},g" -i \ | ||
730 | ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/${sysconfdir}/hostname.erb | ||
731 | #Create an attribute file for /etc/hostname | ||
732 | attr_string="${ERB_DEFAULT_PREFIX}\"HOSTNAME\"${ERB_DEFAULT_SUFFIX} = \"${MY_HOST}\"" | ||
733 | echo "$attr_string" > ${IMAGE_ROOTFS}/${sysconfdir}/${CHEFPN}/hostname-attributes.rb | ||
734 | fi | ||
735 | } | ||
736 | |||
737 | |||
738 | combine_services_daemons(){ | ||
739 | if [ -n $1 ]; then | ||
740 | file_suffix=$1 | ||
741 | rm -f ${DEPLOYCHEF_DIR}/$file_suffix | ||
742 | #combine the list of shutdown/startup scripts | ||
743 | find "${CHEF_ROOT_DIR}/" -name "*$file_suffix" 2> /dev/null | while read fname; do | ||
744 | service_cont=$(cat $fname) | ||
745 | for line in $service_cont; do | ||
746 | service=$(echo $line | awk -F"[SK][0-9]+" '{print $2}') | ||
747 | if [ -e ${INITD_DIR}/$service ]; then | ||
748 | echo $line >> ${DEPLOYCHEF_DIR}/$file_suffix | ||
749 | fi | ||
750 | done | ||
751 | done | ||
752 | fi | ||
753 | } | ||
754 | |||
755 | #This function combines the attributes of all the sevices into | ||
756 | #a default.rb attributes file. | ||
757 | combine_services_attributes(){ | ||
758 | file_suffix='attributes.rb' | ||
759 | mkdir -p ${ATTRIBUTES_DIR}; rm -f ${ATTRIBUTES_FILE} 2>/dev/null | ||
760 | #combine the list of shutdown/starup scripts | ||
761 | find "${CHEF_ROOT_DIR}/" -name "*$file_suffix" 2> /dev/null | \ | ||
762 | while read fname; do | ||
763 | cat $fname | while read line_in_file; do | ||
764 | index=$(echo $line_in_file | awk -F'"' '{print $2}') | ||
765 | #Only append attributes that are not in the default.rb attributes file | ||
766 | if [ ! `grep -l $index ${ATTRIBUTES_FILE}` ]; then | ||
767 | echo $line_in_file >> ${ATTRIBUTES_FILE} | ||
768 | fi | ||
769 | done | ||
770 | done | ||
771 | } | ||
772 | |||
773 | #This function copies the templates to deploychef directory from | ||
774 | #within the packages directories | ||
775 | copy_templates_in_place(){ | ||
776 | #copy rpm-postinsts and config templates into templates directory | ||
777 | mkdir -p ${DEPLOYCHEF_TEMPLATES_DIR} | ||
778 | #First copy all our configuration template files | ||
779 | if [ -d ${CHEF_CONF_DIR} ]; then | ||
780 | cp -rf ${CHEF_CONF_DIR} ${DEPLOYCHEF_TEMPLATES_DIR} | ||
781 | fi | ||
782 | #Now copy the rpm-postinsts files into cookbooks/templates/default/etc/ | ||
783 | if [ -d ${POSTINSTS_DIR} ]; then | ||
784 | cp -rf ${POSTINSTS_DIR} ${DEPLOYCHEF_TEMPLATES_DIR}/${sysconfdir} | ||
785 | #Move the files to base of the templates directory, where chef-solo | ||
786 | #expects them | ||
787 | cp -f ${POSTINSTS_DIR}/* ${DEPLOYCHEF_TEMPLATES_DIR}/. | ||
788 | fi | ||
789 | |||
790 | } | ||
791 | |||
792 | filter_node_dependent_templates(){ | ||
793 | if [ -d ${DEPLOYCHEF_TEMPLATES_DIR} ]; then | ||
794 | find "${DEPLOYCHEF_TEMPLATES_DIR}/" -name "*.erb*" 2> /dev/null | \ | ||
795 | while read fname; do | ||
796 | config_file=$(echo $fname | awk -F'/default' '{print $2}' | awk \ | ||
797 | -F'.erb' '{print $1}') | ||
798 | #If the base configuration file does not exist on this node | ||
799 | #remove it. | ||
800 | if [ ! -f ${IMAGE_ROOTFS}$config_file ]; then | ||
801 | rm -f "$fname" | ||
802 | else | ||
803 | #Move the file to the default template directory where | ||
804 | #chefsolo expect them | ||
805 | cp "$fname" "${DEPLOYCHEF_TEMPLATES_DIR}" | ||
806 | fi | ||
807 | done | ||
808 | fi | ||
809 | } | ||
810 | |||
811 | #This function is our post rootfs hook, it enables | ||
812 | #us to do what we wish to do during rootfs creation process. | ||
813 | deploychef_rootfs_postprocess_commands() { | ||
814 | |||
815 | if [ -n "${OPENSTACKCHEF_ENABLED}" ]; then | ||
816 | deploychef_copy_host_files | ||
817 | combine_services_daemons 'shutdown-list' | ||
818 | combine_services_daemons 'startup-list' | ||
819 | combine_services_attributes | ||
820 | copy_templates_in_place | ||
821 | filter_node_dependent_templates | ||
822 | else | ||
823 | #Let us delete the deploychef init script that runs | ||
824 | #chef-solo at boot-up from rootfs | ||
825 | rm -f ${INITD_DIR}/deploychef 2> /dev/null | ||
826 | fi | ||
827 | #We nolonger have need for /etc/${CHEFPN} directory on rootfs | ||
828 | #Not even at run-time | ||
829 | rm -rf "${CHEF_ROOT_DIR}" | ||
830 | } | ||
831 | |||