diff options
author | Mustapha Lansana <Mustapha.Lansana@windriver.com> | 2014-07-30 19:08:55 -0400 |
---|---|---|
committer | Bruce Ashfield <bruce.ashfield@windriver.com> | 2014-09-26 09:09:21 -0400 |
commit | caf76c8322f247d0829dc9a18009bee0b1195a06 (patch) | |
tree | 84751c17810440a9e560d486f57d9dc16074faf1 /meta-openstack/classes | |
parent | ef4a3055abee950950c2aac127854eb9e83f2793 (diff) | |
download | meta-cloud-services-caf76c8322f247d0829dc9a18009bee0b1195a06.tar.gz |
openstackchef: decentralized openstack-chef class implementation
This class provides a framework for recipes(packages) to register
their configuration files with the deploychef package. This class
works together with the deploychef package. This implementation
makes it possible for the deploychef package, with the help of
chef-solo, to recreate registered configuration files at run-time.
Only services that inherit this class and register their configuration
files are re-configurable at run-time; and by configurable, we mean to
make chefsolo templates out of their configuration files and to be aware
of the list of daemons that need to be killed/started at run-time.
Therefore, openstackchef class requires the recipes/classes inheriting
it to advertise their configuration files, list of start/stop daemons
and finally a special callback function when necessary.
In order to avoid duplication of common placeholders and
substitution across the various recipes inheriting this class,
we keep a dictionary of common placeholders and substitutions,
but rely on the recipes to define the values these placeholders take
at run-time.
See the description at the top of the class files (openstackchef.bbclass
and openstackchef_inc.bbclass ) for more details.
This class makes chef-solo template files out of the configuration
files exposed to it by the recipes.
The resulting templates are stored at
/opt/deploychef/cookbooks/openstack/templates/default.
on the resulting rootfs.
It also creates a file containing a set of chef-solo default template
values known as attributes in chef-solo world. The default values are
provided by the recipes and classes inheriting this class.
The chefsolo attributes file is stored at
/opt/deploychef/cookbooks/openstack/attributes/default.rb
at the end of rootfs creation process.
At run-time, the deploychef package whose package files are stored
at '/opt/deploychef', makes a call to chef-solo.
Chef-solo in turn uses the attributes and templates files to overwrite
the configuration files for services like neutron, nova, swift, etc..
that registered configuration files with openstackchef class
at build time. See accompanying documentation for further explanation.
Signed-off-by: Mustapha Lansana <Mustapha.Lansana@windriver.com>
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 | |||