Browse Source

Initial commit

Change-Id: Ic4a2603e5e23671e3cc6df0e0fee9f80c9921e6c
Andrey Shestakov 3 years ago
parent
commit
f08dc6819e
52 changed files with 2544 additions and 0 deletions
  1. 6
    0
      .gitmodules
  2. 202
    0
      LICENSE
  3. 4
    0
      README.md
  4. 51
    0
      deployment_scripts/fuel-bootstrap-image-builder/Makefile
  5. 448
    0
      deployment_scripts/fuel-bootstrap-image-builder/bin/fuel-bootstrap-image
  6. 21
    0
      deployment_scripts/fuel-bootstrap-image-builder/configure
  7. 38
    0
      deployment_scripts/fuel-bootstrap-image-builder/fuel-bootstrap-image-builder.spec
  8. 13
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/e2fsck.conf
  9. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/hostname
  10. 7
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/init/ironic-callback.conf
  11. BIN
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/localtime
  12. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/modprobe.d/blacklist-i2c-piix4.conf
  13. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/modprobe.d/mlnx4_core.conf
  14. 20
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/ssh/sshd_config
  15. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/root/.ssh/authorized_keys
  16. 13
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/e2fsck.conf
  17. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/hostname
  18. BIN
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/localtime
  19. 28
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/logrotate.d/syslog
  20. 27
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/mcollective/server.cfg
  21. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/modprobe.d/blacklist-i2c-piix4.conf
  22. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/modprobe.d/mlnx4_core.conf
  23. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/nailgun_systemtype
  24. 6
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/rc.local
  25. 6
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/rsyslog.d/50-default-template.conf
  26. 20
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/send2syslog.conf
  27. 20
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/ssh/sshd_config
  28. 1
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/root/.ssh/authorized_keys
  29. 30
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/usr/bin/fix-configs-on-startup
  30. 505
    0
      deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/usr/bin/send2syslog.py
  31. 38
    0
      deployment_scripts/post_swift_key.rb
  32. 51
    0
      deployment_scripts/puppet/manifests/db.pp
  33. 58
    0
      deployment_scripts/puppet/manifests/haproxy.pp
  34. 91
    0
      deployment_scripts/puppet/manifests/ironic-compute.pp
  35. 118
    0
      deployment_scripts/puppet/manifests/ironic-conductor.pp
  36. 96
    0
      deployment_scripts/puppet/manifests/ironic.pp
  37. 54
    0
      deployment_scripts/puppet/manifests/network-conductor.pp
  38. 21
    0
      deployment_scripts/puppet/manifests/network-ovs.pp
  39. 140
    0
      deployment_scripts/puppet/manifests/network.pp
  40. 1
    0
      deployment_scripts/puppet/modules/ironic
  41. 1
    0
      deployment_scripts/puppet/modules/tftp
  42. 141
    0
      deployment_scripts/upload_images.rb
  43. 131
    0
      deployment_tasks.yaml
  44. 31
    0
      environment_config.yaml
  45. 30
    0
      metadata.yaml
  46. 24
    0
      network_roles.yaml
  47. 11
    0
      node_roles.yaml
  48. 23
    0
      post_install.sh
  49. 5
    0
      pre_build_hook
  50. 0
    0
      repositories/centos/.gitkeep
  51. 0
    0
      repositories/ubuntu/.gitkeep
  52. 5
    0
      volumes.yaml

+ 6
- 0
.gitmodules View File

@@ -0,0 +1,6 @@
1
+[submodule "deployment_scripts/puppet/modules/tftp"]
2
+	path = deployment_scripts/puppet/modules/tftp
3
+	url = https://github.com/puppetlabs/puppetlabs-tftp
4
+[submodule "deployment_scripts/puppet/modules/ironic"]
5
+	path = deployment_scripts/puppet/modules/ironic
6
+	url = https://github.com/openstack/puppet-ironic

+ 202
- 0
LICENSE View File

@@ -0,0 +1,202 @@
1
+Apache License
2
+                           Version 2.0, January 2004
3
+                        http://www.apache.org/licenses/
4
+
5
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+   1. Definitions.
8
+
9
+      "License" shall mean the terms and conditions for use, reproduction,
10
+      and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+      "Licensor" shall mean the copyright owner or entity authorized by
13
+      the copyright owner that is granting the License.
14
+
15
+      "Legal Entity" shall mean the union of the acting entity and all
16
+      other entities that control, are controlled by, or are under common
17
+      control with that entity. For the purposes of this definition,
18
+      "control" means (i) the power, direct or indirect, to cause the
19
+      direction or management of such entity, whether by contract or
20
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+      outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+      "You" (or "Your") shall mean an individual or Legal Entity
24
+      exercising permissions granted by this License.
25
+
26
+      "Source" form shall mean the preferred form for making modifications,
27
+      including but not limited to software source code, documentation
28
+      source, and configuration files.
29
+
30
+      "Object" form shall mean any form resulting from mechanical
31
+      transformation or translation of a Source form, including but
32
+      not limited to compiled object code, generated documentation,
33
+      and conversions to other media types.
34
+
35
+      "Work" shall mean the work of authorship, whether in Source or
36
+      Object form, made available under the License, as indicated by a
37
+      copyright notice that is included in or attached to the work
38
+      (an example is provided in the Appendix below).
39
+
40
+      "Derivative Works" shall mean any work, whether in Source or Object
41
+      form, that is based on (or derived from) the Work and for which the
42
+      editorial revisions, annotations, elaborations, or other modifications
43
+      represent, as a whole, an original work of authorship. For the purposes
44
+      of this License, Derivative Works shall not include works that remain
45
+      separable from, or merely link (or bind by name) to the interfaces of,
46
+      the Work and Derivative Works thereof.
47
+
48
+      "Contribution" shall mean any work of authorship, including
49
+      the original version of the Work and any modifications or additions
50
+      to that Work or Derivative Works thereof, that is intentionally
51
+      submitted to Licensor for inclusion in the Work by the copyright owner
52
+      or by an individual or Legal Entity authorized to submit on behalf of
53
+      the copyright owner. For the purposes of this definition, "submitted"
54
+      means any form of electronic, verbal, or written communication sent
55
+      to the Licensor or its representatives, including but not limited to
56
+      communication on electronic mailing lists, source code control systems,
57
+      and issue tracking systems that are managed by, or on behalf of, the
58
+      Licensor for the purpose of discussing and improving the Work, but
59
+      excluding communication that is conspicuously marked or otherwise
60
+      designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+      "Contributor" shall mean Licensor and any individual or Legal Entity
63
+      on behalf of whom a Contribution has been received by Licensor and
64
+      subsequently incorporated within the Work.
65
+
66
+   2. Grant of Copyright License. Subject to the terms and conditions of
67
+      this License, each Contributor hereby grants to You a perpetual,
68
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+      copyright license to reproduce, prepare Derivative Works of,
70
+      publicly display, publicly perform, sublicense, and distribute the
71
+      Work and such Derivative Works in Source or Object form.
72
+
73
+   3. Grant of Patent License. Subject to the terms and conditions of
74
+      this License, each Contributor hereby grants to You a perpetual,
75
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+      (except as stated in this section) patent license to make, have made,
77
+      use, offer to sell, sell, import, and otherwise transfer the Work,
78
+      where such license applies only to those patent claims licensable
79
+      by such Contributor that are necessarily infringed by their
80
+      Contribution(s) alone or by combination of their Contribution(s)
81
+      with the Work to which such Contribution(s) was submitted. If You
82
+      institute patent litigation against any entity (including a
83
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+      or a Contribution incorporated within the Work constitutes direct
85
+      or contributory patent infringement, then any patent licenses
86
+      granted to You under this License for that Work shall terminate
87
+      as of the date such litigation is filed.
88
+
89
+   4. Redistribution. You may reproduce and distribute copies of the
90
+      Work or Derivative Works thereof in any medium, with or without
91
+      modifications, and in Source or Object form, provided that You
92
+      meet the following conditions:
93
+
94
+      (a) You must give any other recipients of the Work or
95
+          Derivative Works a copy of this License; and
96
+
97
+      (b) You must cause any modified files to carry prominent notices
98
+          stating that You changed the files; and
99
+
100
+      (c) You must retain, in the Source form of any Derivative Works
101
+          that You distribute, all copyright, patent, trademark, and
102
+          attribution notices from the Source form of the Work,
103
+          excluding those notices that do not pertain to any part of
104
+          the Derivative Works; and
105
+
106
+      (d) If the Work includes a "NOTICE" text file as part of its
107
+          distribution, then any Derivative Works that You distribute must
108
+          include a readable copy of the attribution notices contained
109
+          within such NOTICE file, excluding those notices that do not
110
+          pertain to any part of the Derivative Works, in at least one
111
+          of the following places: within a NOTICE text file distributed
112
+          as part of the Derivative Works; within the Source form or
113
+          documentation, if provided along with the Derivative Works; or,
114
+          within a display generated by the Derivative Works, if and
115
+          wherever such third-party notices normally appear. The contents
116
+          of the NOTICE file are for informational purposes only and
117
+          do not modify the License. You may add Your own attribution
118
+          notices within Derivative Works that You distribute, alongside
119
+          or as an addendum to the NOTICE text from the Work, provided
120
+          that such additional attribution notices cannot be construed
121
+          as modifying the License.
122
+
123
+      You may add Your own copyright statement to Your modifications and
124
+      may provide additional or different license terms and conditions
125
+      for use, reproduction, or distribution of Your modifications, or
126
+      for any such Derivative Works as a whole, provided Your use,
127
+      reproduction, and distribution of the Work otherwise complies with
128
+      the conditions stated in this License.
129
+
130
+   5. Submission of Contributions. Unless You explicitly state otherwise,
131
+      any Contribution intentionally submitted for inclusion in the Work
132
+      by You to the Licensor shall be under the terms and conditions of
133
+      this License, without any additional terms or conditions.
134
+      Notwithstanding the above, nothing herein shall supersede or modify
135
+      the terms of any separate license agreement you may have executed
136
+      with Licensor regarding such Contributions.
137
+
138
+   6. Trademarks. This License does not grant permission to use the trade
139
+      names, trademarks, service marks, or product names of the Licensor,
140
+      except as required for reasonable and customary use in describing the
141
+      origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+   7. Disclaimer of Warranty. Unless required by applicable law or
144
+      agreed to in writing, Licensor provides the Work (and each
145
+      Contributor provides its Contributions) on an "AS IS" BASIS,
146
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+      implied, including, without limitation, any warranties or conditions
148
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+      PARTICULAR PURPOSE. You are solely responsible for determining the
150
+      appropriateness of using or redistributing the Work and assume any
151
+      risks associated with Your exercise of permissions under this License.
152
+
153
+   8. Limitation of Liability. In no event and under no legal theory,
154
+      whether in tort (including negligence), contract, or otherwise,
155
+      unless required by applicable law (such as deliberate and grossly
156
+      negligent acts) or agreed to in writing, shall any Contributor be
157
+      liable to You for damages, including any direct, indirect, special,
158
+      incidental, or consequential damages of any character arising as a
159
+      result of this License or out of the use or inability to use the
160
+      Work (including but not limited to damages for loss of goodwill,
161
+      work stoppage, computer failure or malfunction, or any and all
162
+      other commercial damages or losses), even if such Contributor
163
+      has been advised of the possibility of such damages.
164
+
165
+   9. Accepting Warranty or Additional Liability. While redistributing
166
+      the Work or Derivative Works thereof, You may choose to offer,
167
+      and charge a fee for, acceptance of support, warranty, indemnity,
168
+      or other liability obligations and/or rights consistent with this
169
+      License. However, in accepting such obligations, You may act only
170
+      on Your own behalf and on Your sole responsibility, not on behalf
171
+      of any other Contributor, and only if You agree to indemnify,
172
+      defend, and hold each Contributor harmless for any liability
173
+      incurred by, or claims asserted against, such Contributor by reason
174
+      of your accepting any such warranty or additional liability.
175
+
176
+   END OF TERMS AND CONDITIONS
177
+
178
+   APPENDIX: How to apply the Apache License to your work.
179
+
180
+      To apply the Apache License to your work, attach the following
181
+      boilerplate notice, with the fields enclosed by brackets "{}"
182
+      replaced with your own identifying information. (Don't include
183
+      the brackets!)  The text should be enclosed in the appropriate
184
+      comment syntax for the file format. We also recommend that a
185
+      file or class name and description of purpose be included on the
186
+      same "printed page" as the copyright notice for easier
187
+      identification within third-party archives.
188
+
189
+   Copyright {yyyy} {name of copyright owner}
190
+
191
+   Licensed under the Apache License, Version 2.0 (the "License");
192
+   you may not use this file except in compliance with the License.
193
+   You may obtain a copy of the License at
194
+
195
+       http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+   Unless required by applicable law or agreed to in writing, software
198
+   distributed under the License is distributed on an "AS IS" BASIS,
199
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+   See the License for the specific language governing permissions and
201
+   limitations under the License.
202
+

+ 4
- 0
README.md View File

@@ -0,0 +1,4 @@
1
+fuel-plugin-ironic
2
+============
3
+
4
+Plugin description

+ 51
- 0
deployment_scripts/fuel-bootstrap-image-builder/Makefile View File

@@ -0,0 +1,51 @@
1
+
2
+
3
+VERSION?=7.0.0
4
+
5
+top_srcdir:=$(shell pwd)
6
+ubuntu_DATA:=$(shell cd $(top_srcdir) && find share -type f)
7
+top_builddir?=$(shell pwd)
8
+-include config.mk
9
+PREFIX?=/usr
10
+
11
+all:
12
+	@echo nop
13
+
14
+install:
15
+	install -d -m 755 $(DESTDIR)$(PREFIX)/bin
16
+	install -d -m 755 $(DESTDIR)$(PREFIX)/share/fuel-bootstrap-image
17
+	install -m 755 -t $(DESTDIR)$(PREFIX)/bin $(top_srcdir)/bin/fuel-bootstrap-image
18
+	tar cf - -C $(top_srcdir) share | tar xf - -C $(DESTDIR)$(PREFIX)
19
+
20
+dist: $(top_builddir)/fuel-bootstrap-image-builder-$(VERSION).tar.gz
21
+
22
+$(top_builddir)/fuel-bootstrap-image-builder-$(VERSION).tar.gz: STAGEDIR:=$(top_builddir)/dist/fuel-bootstrap-image-builder
23
+$(top_builddir)/fuel-bootstrap-image-builder-$(VERSION).tar.gz: bin/fuel-bootstrap-image $(ubuntu_DATA) Makefile configure
24
+	mkdir -p $(STAGEDIR)/share
25
+	mkdir -p $(STAGEDIR)/bin
26
+	tar cf - -C $(top_srcdir) bin share | tar xf - -C $(STAGEDIR)
27
+	cp -a $(top_srcdir)/Makefile $(top_srcdir)/configure $(top_srcdir)/fuel-bootstrap-image-builder.spec $(STAGEDIR)
28
+	tar czf $@.tmp -C $(dir $(STAGEDIR)) $(notdir $(STAGEDIR))
29
+	mv $@.tmp $@
30
+
31
+rpm: SANDBOX:=$(top_builddir)/rpmbuild
32
+rpm: $(top_builddir)/fuel-bootstrap-image-builder-$(VERSION).tar.gz fuel-bootstrap-image-builder.spec
33
+	rm -rf $(SANDBOX)
34
+	mkdir -p $(SANDBOX)/SOURCES $(SANDBOX)/SPECS $(SANDBOX)/tmp
35
+	cp -a $< $(SANDBOX)/SOURCES
36
+	cp -a $(top_srcdir)/fuel-bootstrap-image-builder.spec $(SANDBOX)/SPECS
37
+	fakeroot rpmbuild --nodeps \
38
+		--define '_tmppath $(SANDBOX)/tmp' \
39
+		--define '_topdir $(SANDBOX)' \
40
+		--define 'version $(VERSION)' \
41
+		-ba $(SANDBOX)/SPECS/fuel-bootstrap-image-builder.spec
42
+
43
+clean:
44
+	-@rm -f $(top_builddir)/config.mk
45
+
46
+distclean: clean
47
+	-@rm -f $(top_builddir)/fuel-bootstrap-image-builder-$(VERSION).tar.gz
48
+	-@rm -rf $(top_builddir)/rpmbuild
49
+	-@rm -rf $(top_builddir)/dist
50
+
51
+.PHONY: all install dist clean rpm

+ 448
- 0
deployment_scripts/fuel-bootstrap-image-builder/bin/fuel-bootstrap-image View File

@@ -0,0 +1,448 @@
1
+#!/bin/sh
2
+set -ex
3
+MYSELF="${0##*/}"
4
+
5
+bindir="${0%/*}"
6
+datadir="${bindir%/*}/share/fuel-bootstrap-image"
7
+
8
+global_conf="/etc/fuel-bootstrap-image.conf"
9
+[ -r "$global_conf" ] && . "$global_conf"
10
+
11
+[ -z "$MOS_VERSION" ] && MOS_VERSION="7.0"
12
+[ -z "$DISTRO_RELEASE" ] && DISTRO_RELEASE="trusty"
13
+[ -z "$MIRROR_DISTRO" ] && MIRROR_DISTRO="http://archive.ubuntu.com/ubuntu"
14
+[ -z "$MIRROR_MOS" ] && MIRROR_MOS="http://mirror.fuel-infra.org/mos-repos/$MOS_VERSION/cluster/base/$DISTRO_RELEASE"
15
+[ -z "$KERNEL_FLAVOR" ] && KERNEL_FLAVOR="-generic-lts-trusty"
16
+[ -z "$ARCH" ] && ARCH="amd64"
17
+[ -z "$DESTDIR" ] && DESTDIR="/var/www/nailgun/bootstrap/ubuntu"
18
+[ -z "$BOOTSTRAP_SSH_KEYS" ] && BOOTSTRAP_SSH_KEYS="$datadir/ubuntu/files/root/.ssh/authorized_keys"
19
+
20
+BOOTSTRAP_FUEL_PKGS_DFLT="openssh-server ntp"
21
+
22
+# Packages required for the master node to discover a bootstrap node
23
+if [ -z "$BOOTSTRAP_IRONIC" ]; then
24
+	BOOTSTRAP_FUEL_PKGS_DFLT="$BOOTSTRAP_FUEL_PKGS_DFLT openssh-client mcollective nailgun-agent nailgun-mcagents nailgun-net-check"
25
+	GONFIG_SOURCE="$datadir/ubuntu/files/"
26
+else
27
+	GONFIG_SOURCE="$datadir/ubuntu/files.ironic/"
28
+fi
29
+
30
+[ -z "$BOOTSTRAP_FUEL_PKGS" ] && BOOTSTRAP_FUEL_PKGS="$BOOTSTRAP_FUEL_PKGS_DFLT"
31
+
32
+if [ -n "$http_proxy" ]; then
33
+	export HTTP_PROXY="$http_proxy"
34
+elif [ -n "$HTTP_PROXY" ]; then
35
+	export http_proxy="$HTTP_PROXY"
36
+fi
37
+
38
+# Kernel, firmware, live boot
39
+BOOTSTRAP_PKGS="ubuntu-minimal live-boot live-boot-initramfs-tools linux-image${KERNEL_FLAVOR} linux-firmware linux-firmware-nonfree"
40
+# compress initramfs with xz, make squashfs root filesystem image
41
+BOOTSTRAP_PKGS="$BOOTSTRAP_PKGS xz-utils squashfs-tools"
42
+# Smaller tools providing the standard ones.
43
+# - mdadm depends on mail-transport-agent, default one is postfix => use msmtp instead
44
+BOOTSTRAP_PKGS="$BOOTSTRAP_PKGS msmtp-mta gdebi-core"
45
+
46
+apt_setup ()
47
+{
48
+	local root="$1"
49
+	local sources_list="${root}/etc/apt/sources.list"
50
+	local apt_prefs="${root}/etc/apt/preferences"
51
+	local mos_codename="mos${MOS_VERSION}-${DISTRO_RELEASE}"
52
+	local broken_repo=''
53
+	local release_file="$MIRROR_MOS/dists/$mos_codename/Release"
54
+	if ! wget -q -O /dev/null "$release_file" 2>/dev/null; then
55
+		broken_repo='yes'
56
+	fi
57
+	mkdir -p "${sources_list%/*}"
58
+
59
+	cat > "$sources_list" <<-EOF
60
+	deb $MIRROR_DISTRO ${DISTRO_RELEASE}         main universe multiverse restricted
61
+	deb $MIRROR_DISTRO ${DISTRO_RELEASE}-security main universe multiverse restricted
62
+	deb $MIRROR_DISTRO ${DISTRO_RELEASE}-updates  main universe multiverse restricted
63
+	EOF
64
+	if [ -z "$broken_repo" ]; then
65
+		cat >> "$sources_list" <<-EOF
66
+		deb $MIRROR_MOS ${mos_codename}          main
67
+		deb $MIRROR_MOS ${mos_codename}-security main
68
+		deb $MIRROR_MOS ${mos_codename}-updates  main
69
+		deb $MIRROR_MOS ${mos_codename}-holdback main
70
+		EOF
71
+	else
72
+		# TODO(asheplyakov): remove this after perestroika repo gets fixed
73
+		cat >> "$sources_list" <<-EOF
74
+		deb $MIRROR_MOS ${DISTRO_RELEASE} main
75
+		EOF
76
+	fi
77
+	if [ -n "$EXTRA_DEB_REPOS" ]; then
78
+		l="$EXTRA_DEB_REPOS"
79
+		IFS='|'
80
+		set -- $l
81
+		unset IFS
82
+		for repo; do
83
+			echo "$repo"
84
+		done >> "$sources_list"
85
+	fi
86
+
87
+	cat > "$apt_prefs" <<-EOF
88
+	Package: *
89
+	Pin: release o=Mirantis, n=mos${MOS_VERSION}
90
+	Pin-Priority: 1101
91
+	Package: *
92
+	Pin: release o=Mirantis, n=${DISTRO_RELEASE}
93
+	Pin-Priority: 1101
94
+	EOF
95
+
96
+	if [ -n "$HTTP_PROXY" ]; then
97
+		cat > "$root/etc/apt/apt.conf.d/01mirantis-use-proxy" <<-EOF
98
+		Acquire::http::Proxy "$HTTP_PROXY";
99
+		EOF
100
+	fi
101
+}
102
+
103
+run_apt_get ()
104
+{
105
+	local root="$1"
106
+	shift
107
+	chroot "$root" env \
108
+		LC_ALL=C \
109
+		DEBIAN_FRONTEND=noninteractive \
110
+		DEBCONF_NONINTERACTIVE_SEEN=true \
111
+		TMPDIR=/tmp \
112
+		TMP=/tmp \
113
+		apt-get $@
114
+}
115
+
116
+run_apt_key ()
117
+{
118
+	local root="$1"
119
+	shift
120
+	chroot "$root" env \
121
+		LC_ALL=C \
122
+		DEBIAN_FRONTEND=noninteractive \
123
+		DEBCONF_NONINTERACTIVE_SEEN=true \
124
+		TMPDIR=/tmp \
125
+		TMP=/tmp \
126
+		apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $@
127
+}
128
+
129
+dpkg_is_too_old ()
130
+{
131
+	# XXX: dpkg-deb versions older than 1.15.6 can't handle data.tar.xz
132
+	# (which is the default payload of Ubuntu packages)
133
+	# Such an ancient version of dpkg is shipped with CentOS 6.[56]
134
+	local dpkg_version
135
+	local dpkg_major_version
136
+	local dpkg_minor_version
137
+	local dpkg_patch_version
138
+	if ! dpkg-deb --help >/dev/null 2>&1; then
139
+		return 0
140
+	fi
141
+	dpkg_version=`dpkg-deb --version | sed -rne '1 s/^.*\s+version\s+([0-9]+)\.([0-9]+)\.([0-9]+).*/\1.\2.\3/p'`
142
+	[ -z "$dpkg_version" ] && return 0
143
+
144
+	IFS='.'
145
+	set -- $dpkg_version
146
+	unset IFS
147
+	dpkg_major_version="$1"
148
+	dpkg_minor_version="$2"
149
+	dpkg_patch_version="$3"
150
+
151
+	if [ $dpkg_major_version -le 1 ] && [ $dpkg_minor_version -le 15 ] && [ $dpkg_patch_version -lt 6 ]; then
152
+		echo "DEBUG: $MYSELF: dpkg is too old, using ar to unpack debian packages" >&2
153
+		return 0
154
+	fi
155
+	return 1
156
+}
157
+
158
+run_debootstrap ()
159
+{
160
+	local root="$1"
161
+	[ -z "$root" ] && exit 1
162
+	local insecure="--no-check-gpg"
163
+	local extractor=''
164
+	if dpkg_is_too_old; then
165
+		# Ubuntu packages use data.tar.xz payload. Ancient versions of
166
+		# dpkg (in particular the ones shipped with CentOS 6.x) can't
167
+		# handle such packages. Tell debootstrap to use ar instead to
168
+		# avoid the failure.
169
+		extractor='--extractor=ar'
170
+	fi
171
+	env \
172
+		LC_ALL=C \
173
+		DEBIAN_FRONTEND=noninteractive \
174
+		DEBCONF_NONINTERACTIVE_SEEN=true \
175
+		debootstrap $insecure $extractor --arch=${ARCH} ${DISTRO_RELEASE} "$root" $MIRROR_DISTRO
176
+}
177
+
178
+install_packages ()
179
+{
180
+	local root="$1"
181
+	shift
182
+	echo "INFO: $MYSELF: installing pkgs: $*" >&2
183
+	run_apt_get "$root" install --yes $@
184
+}
185
+
186
+upgrade_chroot ()
187
+{
188
+	local root="$1"
189
+	run_apt_key "$root" CA2B20483E301371
190
+	run_apt_get "$root" update
191
+	if ! mountpoint -q "$root/proc"; then
192
+		mount -t proc bootstrapproc "$root/proc"
193
+	fi
194
+	run_apt_get "$root" dist-upgrade --yes
195
+}
196
+
197
+add_local_mos_repo ()
198
+{
199
+	# we need the local APT repo (/var/www/nailgun/ubuntu/x86_64)
200
+	# before web server is up and running => use bind mount
201
+	local root="$1"
202
+	# TODO(asheplyakov): use proper arch name (amd64)
203
+	local local_repo="/var/www/nailgun/ubuntu/x86_64"
204
+	local path_in_chroot="/tmp/local-apt"
205
+	local source_parts_d="${root}/etc/apt/sources.list.d"
206
+
207
+	# TODO(asheplyakov): update the codename after repo get fixed
208
+	local mos_codename="mos${MOS_VERSION}"
209
+
210
+	mkdir -p "${root}${path_in_chroot}" "${source_parts_d}"
211
+	mount -o bind "$local_repo" "${root}${path_in_chroot}"
212
+	mount -o remount,ro,bind "${root}${path_in_chroot}"
213
+	cat > "${source_parts_d}/nailgun-local.list" <<-EOF
214
+	deb file://${path_in_chroot} ${mos_codename} main
215
+	EOF
216
+}
217
+
218
+allow_insecure_apt ()
219
+{
220
+	local root="$1"
221
+	local conflet="${root}/etc/apt/apt.conf.d/02mirantis-insecure-apt"
222
+	mkdir -p "${conflet%/*}"
223
+	echo 'APT::Get::AllowUnauthenticated 1;' > "$conflet"
224
+}
225
+
226
+suppress_services_start ()
227
+{
228
+	local root="$1"
229
+	local policy_rc="$root/usr/sbin/policy-rc.d"
230
+	mkdir -p "${policy_rc%/*}"
231
+	cat > "$policy_rc" <<-EOF
232
+	#!/bin/sh
233
+	# suppress services start in the staging chroot
234
+	exit 101
235
+	EOF
236
+	chmod 755 "$policy_rc"
237
+}
238
+
239
+propagate_host_resolv_conf ()
240
+{
241
+	local root="$1"
242
+	mkdir -p "$root/etc"
243
+	for conf in "/etc/resolv.conf" "/etc/hosts"; do
244
+		if [ -e "${root}${conf}" ]; then
245
+			cp -a "${root}${conf}" "${root}${conf}.bak"
246
+		fi
247
+	done
248
+}
249
+
250
+restore_resolv_conf ()
251
+{
252
+	local root="$1"
253
+	for conf in "/etc/resolv.conf" "/etc/hosts"; do
254
+		if [ -e "${root}${conf}.bak" ]; then
255
+			rm -f "${root}${conf}"
256
+			cp -a "${root}${conf}.bak" "${root}${conf}"
257
+		fi
258
+	done
259
+}
260
+
261
+make_utf8_locale ()
262
+{
263
+	local root="$1"
264
+	chroot "$root" /bin/sh -c "locale-gen en_US.UTF-8 && dpkg-reconfigure locales"
265
+}
266
+
267
+copy_conf_files ()
268
+{
269
+	local root="$1"
270
+	local sdir="$2"
271
+	rsync -rlptDK "${sdir}" "${root%/}"
272
+	sed -i $root/etc/shadow -e '/^root/c\root:$$6$$oC7haQNQ$$LtVf6AI.QKn9Jb89r83PtQN9fBqpHT9bAFLzy.YVxTLiFgsoqlPY3awKvbuSgtxYHx4RUcpUqMotp.WZ0Hwoj.:15441:0:99999:7:::'
273
+}
274
+
275
+install_ssh_keys ()
276
+{
277
+	local root="$1"
278
+	shift
279
+	if [ -z "$*" ]; then
280
+		echo "*** Error: $MYSELF: no ssh keys specified" >&2
281
+		exit 1
282
+	fi
283
+	local authorized_keys="$root/root/.ssh/authorized_keys"
284
+	local dot_ssh_dir="${authorized_keys%/*}"
285
+	if [ ! -d "${dot_ssh_dir}" ]; then
286
+		mkdir -p -m700 "${dot_ssh_dir}"
287
+	fi
288
+	for key; do
289
+		if [ ! -r "$key" ]; then
290
+			echo "*** Error: $MYSELF: no such file: $key" >&2
291
+			exit 1
292
+		fi
293
+	done
294
+	cat $@ > "$authorized_keys"
295
+	chmod 640 "$authorized_keys"
296
+}
297
+
298
+cleanup_chroot ()
299
+{
300
+	local root="$1"
301
+	[ -z "$root" ] && exit 1
302
+	signal_chrooted_processes "$root" SIGTERM
303
+	signal_chrooted_processes "$root" SIGKILL
304
+	# umount "${root}/tmp/local-apt" 2>/dev/null || umount -l "${root}/tmp/local-apt"
305
+	# rm -f "${root}/etc/apt/sources.list.d/nailgun-local.list"
306
+	rm -rf $root/var/cache/apt/archives/*.deb
307
+	rm -f $root/etc/apt/apt.conf.d/01mirantis-use-proxy.conf
308
+	rm -f $root/var/log/bootstrap.log
309
+	rm -rf $root/tmp/*
310
+	rm -rf $root/run/*
311
+}
312
+
313
+install_agent ()
314
+{
315
+        local root="$1"
316
+        local package_path="$2"
317
+        local full_path=`ls $package_path/fuel-agent*.deb`
318
+        local package=`basename $full_path`
319
+        cp $full_path $root/tmp
320
+        chroot "$root" env \
321
+                LC_ALL=C \
322
+                DEBIAN_FRONTEND=noninteractive \
323
+                DEBCONF_NONINTERACTIVE_SEEN=true \
324
+                TMPDIR=/tmp \
325
+                TMP=/tmp \
326
+                gdebi -n /tmp/$package
327
+
328
+        rm -f $root/tmp/$package
329
+}
330
+
331
+recompress_initramfs ()
332
+{
333
+	local root="$1"
334
+	local initramfs_conf="$root/etc/initramfs-tools/initramfs.conf"
335
+	sed -i $initramfs_conf -re 's/COMPRESS\s*=\s*gzip/COMPRESS=xz/'
336
+	rm -f $root/boot/initrd*
337
+	chroot "$root" \
338
+		env \
339
+		LC_ALL=C \
340
+		DEBIAN_FRONTEND=noninteractive \
341
+		DEBCONF_NONINTERACTIVE_SEEN=true \
342
+		TMPDIR=/tmp \
343
+		TMP=/tmp \
344
+		update-initramfs -c -k all
345
+}
346
+
347
+mk_squashfs_image ()
348
+{
349
+	local root="$1"
350
+	local tmp="$$"
351
+	[ -d "$DESTDIR" ] && mkdir -p "$DESTDIR"
352
+	cp -a $root/boot/initrd* $DESTDIR/initramfs.img.${tmp}
353
+	cp -a $root/boot/vmlinuz* $DESTDIR/linux.${tmp}
354
+	rm -f $root/boot/initrd*
355
+	rm -f $root/boot/vmlinuz*
356
+
357
+	# run mksquashfs inside a chroot (Ubuntu kernel will be able to
358
+	# mount an image produced by Ubuntu squashfs-tools)
359
+	mount -t tmpfs -o rw,nodev,nosuid,noatime,mode=0755,size=4M mnt${tmp} "$root/mnt"
360
+	mkdir -p "$root/mnt/src" "$root/mnt/dst"
361
+	mount -o bind "$root" "$root/mnt/src"
362
+	mount -o remount,bind,ro "$root/mnt/src"
363
+	mount -o bind "$DESTDIR" "$root/mnt/dst"
364
+
365
+	if ! mountpoint -q "$root/proc"; then
366
+		mount -t proc sandboxproc "$root/proc"
367
+	fi
368
+	chroot "$root" mksquashfs /mnt/src /mnt/dst/root.squashfs.${tmp} -comp xz -no-progress -noappend
369
+	mv $DESTDIR/initramfs.img.${tmp} $DESTDIR/initramfs.img
370
+	mv $DESTDIR/linux.${tmp} $DESTDIR/linux
371
+	mv $DESTDIR/root.squashfs.${tmp} $DESTDIR/root.squashfs
372
+
373
+	umount "$root/mnt/dst"
374
+	umount "$root/mnt/src"
375
+	umount "$root/mnt"
376
+}
377
+
378
+build_image ()
379
+{
380
+	local root="$1"
381
+	chmod 755 "$root"
382
+	suppress_services_start "$root"
383
+	run_debootstrap "$root"
384
+	suppress_services_start "$root"
385
+	propagate_host_resolv_conf "$root"
386
+	make_utf8_locale "$root"
387
+	apt_setup "$root"
388
+	# add_local_mos_repo "$root"
389
+	allow_insecure_apt "$root"
390
+	upgrade_chroot "$root"
391
+	install_packages "$root" $BOOTSTRAP_PKGS $BOOTSTRAP_FUEL_PKGS
392
+	install_agent "$root" $AGENT_PACKAGE_PATH
393
+	recompress_initramfs "$root"
394
+	copy_conf_files "$root" $GONFIG_SOURCE
395
+	install_ssh_keys "$root" $BOOTSTRAP_SSH_KEYS
396
+	restore_resolv_conf "$root"
397
+	cleanup_chroot "$root"
398
+	mk_squashfs_image "$root"
399
+}
400
+
401
+root=`mktemp -d --tmpdir fuel-bootstrap-image.XXXXXXXXX`
402
+
403
+main ()
404
+{
405
+	build_image "$root"
406
+}
407
+
408
+signal_chrooted_processes ()
409
+{
410
+	local root="$1"
411
+	local signal="${2:-SIGTERM}"
412
+	local max_attempts=10
413
+	local timeout=2
414
+	local count=0
415
+	local found_processes
416
+	[ ! -d "$root" ] && return 0
417
+	while [ $count -lt $max_attempts ]; do
418
+		found_processes=''
419
+		for pid in `fuser $root 2>/dev/null`; do
420
+			[ "$pid" = "kernel" ] && continue
421
+			if [ "`readlink /proc/$pid/root`" = "$root" ]; then
422
+				found_processes='yes'
423
+				kill "-${signal}" $pid
424
+			fi
425
+		done
426
+		[ -z "$found_processes" ] && break
427
+		count=$((count+1))
428
+		sleep $timeout
429
+	done
430
+}
431
+
432
+final_cleanup ()
433
+{
434
+	signal_chrooted_processes "$root" SIGTERM
435
+	signal_chrooted_processes "$root" SIGKILL
436
+	for mnt in /tmp/local-apt /mnt/dst /mnt/src /mnt /proc; do
437
+		if mountpoint -q "${root}${mnt}"; then
438
+			umount "${root}${mnt}" || umount -l "${root}${mnt}" || true
439
+		fi
440
+	done
441
+	if [ -z "$SAVE_TEMPS" ]; then
442
+		rm -rf "$root"
443
+	fi
444
+}
445
+
446
+trap final_cleanup 0
447
+trap final_cleanup HUP TERM INT QUIT
448
+main

+ 21
- 0
deployment_scripts/fuel-bootstrap-image-builder/configure View File

@@ -0,0 +1,21 @@
1
+#!/bin/sh
2
+set -e
3
+# Stub configure script to make rpmbuild happy
4
+
5
+PREFIX=''
6
+
7
+for arg; do
8
+	case $arg in
9
+		--prefix)
10
+			shift
11
+			PREFIX="$arg"
12
+			;;
13
+		--prefix=*)
14
+			PREFIX="${arg##--prefix=*}"
15
+			;;
16
+	esac
17
+done
18
+
19
+cat > config.mk <<-EOF
20
+PREFIX:=${PREFIX:-/usr}
21
+EOF

+ 38
- 0
deployment_scripts/fuel-bootstrap-image-builder/fuel-bootstrap-image-builder.spec View File

@@ -0,0 +1,38 @@
1
+%define name fuel-bootstrap-image-builder
2
+%{!?version: %define version 7.0.0}
3
+%{!?release: %define release 1}
4
+
5
+Summary: Fuel bootstrap image generator
6
+Name: %{name}
7
+Version: %{version}
8
+Release: %{release}
9
+URL:     http://github.com/asheplyakov/fuel-bootstrap-image
10
+Source0: fuel-bootstrap-image-builder-%{version}.tar.gz
11
+License: Apache
12
+BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot
13
+Prefix: %{_prefix}
14
+Requires: debootstrap, wget
15
+BuildArch: noarch
16
+
17
+%description
18
+Fuel bootstrap image generator package
19
+
20
+%prep
21
+%autosetup -n %{name}
22
+
23
+%build
24
+%configure
25
+
26
+%install
27
+%make_install
28
+mkdir -p %{buildroot}/var/www/nailgun/bootstrap/ubuntu
29
+
30
+%clean
31
+rm -rf $RPM_BUILD_ROOT
32
+
33
+%files
34
+%defattr(-,root,root)
35
+%{_bindir}/*
36
+%{_datadir}/fuel-bootstrap-image/*
37
+%dir /var/www/nailgun/bootstrap/ubuntu
38
+

+ 13
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/e2fsck.conf View File

@@ -0,0 +1,13 @@
1
+[problems]
2
+
3
+# Superblock last mount time is in the future (PR_0_FUTURE_SB_LAST_MOUNT).
4
+0x000031 = {
5
+    preen_ok = true
6
+    preen_nomessage = true
7
+}
8
+
9
+# Superblock last write time is in the future (PR_0_FUTURE_SB_LAST_WRITE).
10
+0x000032 = {
11
+    preen_ok = true
12
+    preen_nomessage = true
13
+}

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/hostname View File

@@ -0,0 +1 @@
1
+bootstrap

+ 7
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/init/ironic-callback.conf View File

@@ -0,0 +1,7 @@
1
+description "Ironic call back script"
2
+
3
+start on started ssh
4
+
5
+task
6
+
7
+exec /usr/bin/ironic_callback

BIN
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/localtime View File


+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/modprobe.d/blacklist-i2c-piix4.conf View File

@@ -0,0 +1 @@
1
+blacklist i2c_piix4

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/modprobe.d/mlnx4_core.conf View File

@@ -0,0 +1 @@
1
+options mlx4_core port_type_array=2,2

+ 20
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/etc/ssh/sshd_config View File

@@ -0,0 +1,20 @@
1
+Protocol 2
2
+SyslogFacility AUTHPRIV
3
+PasswordAuthentication no
4
+PubkeyAuthentication yes
5
+ChallengeResponseAuthentication no
6
+GSSAPIAuthentication no
7
+UsePAM no
8
+UseDNS no
9
+
10
+# Accept locale-related environment variables
11
+AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
12
+AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
13
+AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
14
+AcceptEnv XMODIFIERS
15
+
16
+Subsystem	sftp	/usr/lib/openssh/sftp-server
17
+
18
+# Secure Ciphers and MACs
19
+Ciphers aes256-ctr,aes192-ctr,aes128-ctr,arcfour256,arcfour128
20
+MACs hmac-sha2-512,hmac-sha2-256,hmac-ripemd160,hmac-sha1

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files.ironic/root/.ssh/authorized_keys View File

@@ -0,0 +1 @@
1
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDtrVTSM8tGd4E8khJn2gfN/2fymnX/0YKAGSVZTWDNIcYL5zXTlSwrccn/8EgmnNsJNxucJRT+oWqrDGaFaehuwlY/IBqm50KJVaUr5QYzOUpqVpFIpoX3UwETCxcSB1LiQYbCvrJcqOPQ4Zu9fMhMGKaAX1ohzOumn4czuLDYIvCnPnoU5RDWt7g1GaFFlzGU3JFooj7/aWFJMqJLinvay3vr2vFpBvO1y29nKu+zgpZkzzJCc0ndoVqvB+W9DY6QtgTSWfd3ZE/8vg4h8QV8H+xxqL/uWCxDkv2Y3rviAHivR/V+1YCSQH0NBJrNSkRjd+1roLhcEGT7/YEnbgVV nailgun@bootstrap

+ 13
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/e2fsck.conf View File

@@ -0,0 +1,13 @@
1
+[problems]
2
+
3
+# Superblock last mount time is in the future (PR_0_FUTURE_SB_LAST_MOUNT).
4
+0x000031 = {
5
+    preen_ok = true
6
+    preen_nomessage = true
7
+}
8
+
9
+# Superblock last write time is in the future (PR_0_FUTURE_SB_LAST_WRITE).
10
+0x000032 = {
11
+    preen_ok = true
12
+    preen_nomessage = true
13
+}

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/hostname View File

@@ -0,0 +1 @@
1
+bootstrap

BIN
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/localtime View File


+ 28
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/logrotate.d/syslog View File

@@ -0,0 +1,28 @@
1
+/var/log/cron
2
+/var/log/maillog
3
+/var/log/messages
4
+/var/log/secure
5
+/var/log/spooler
6
+/var/log/mcollective.log
7
+/var/log/nailgun-agent.log
8
+{
9
+  # This file is used for daily log rotations, do not use size options here
10
+  sharedscripts
11
+  daily
12
+  # rotate only if 30M size or bigger
13
+  minsize 30M
14
+  maxsize 50M
15
+  # truncate file, do not delete & recreate
16
+  copytruncate
17
+  # keep logs for XXX rotations
18
+  rotate 3
19
+  # compression will be postponed to the next rotation, if uncommented
20
+  compress
21
+  # ignore missing files
22
+  missingok
23
+  # do not rotate empty files
24
+  notifempty
25
+  postrotate
26
+    /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
27
+  endscript
28
+}

+ 27
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/mcollective/server.cfg View File

@@ -0,0 +1,27 @@
1
+main_collective = mcollective
2
+collectives = mcollective
3
+libdir = /usr/share/mcollective/plugins
4
+logfile = /var/log/mcollective.log
5
+loglevel = debug
6
+direct_addressing = 1
7
+daemonize = 0
8
+
9
+# Set TTL to 1.5 hours
10
+ttl = 5400
11
+
12
+# Plugins
13
+securityprovider = psk
14
+plugin.psk = unset
15
+
16
+connector = rabbitmq
17
+plugin.rabbitmq.vhost = mcollective
18
+plugin.rabbitmq.pool.size = 1
19
+plugin.rabbitmq.pool.1.host =
20
+plugin.rabbitmq.pool.1.port = 61613
21
+plugin.rabbitmq.pool.1.user = mcollective
22
+plugin.rabbitmq.pool.1.password = marionette
23
+plugin.rabbitmq.heartbeat_interval = 30
24
+
25
+# Facts
26
+factsource = yaml
27
+plugin.yaml = /etc/mcollective/facts.yaml

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/modprobe.d/blacklist-i2c-piix4.conf View File

@@ -0,0 +1 @@
1
+blacklist i2c_piix4

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/modprobe.d/mlnx4_core.conf View File

@@ -0,0 +1 @@
1
+options mlx4_core port_type_array=2,2

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/nailgun_systemtype View File

@@ -0,0 +1 @@
1
+bootstrap

+ 6
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/rc.local View File

@@ -0,0 +1,6 @@
1
+#!/bin/sh -e
2
+
3
+fix-configs-on-startup || true
4
+flock -w 0 -o /var/lock/agent.lock -c "/opt/nailgun/bin/agent >> /var/log/nailgun-agent.log 2>&1" || true
5
+
6
+touch /var/lock/subsys/local

+ 6
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/rsyslog.d/50-default-template.conf View File

@@ -0,0 +1,6 @@
1
+# Log all messages with this template
2
+$template CustomLog, "%$NOW%T%TIMESTAMP:8:$%Z %syslogseverity-text% %syslogtag% %msg%\n"
3
+
4
+$ActionFileDefaultTemplate CustomLog
5
+
6
+user.debug  /var/log/messages

+ 20
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/send2syslog.conf View File

@@ -0,0 +1,20 @@
1
+{
2
+"watchlist": [
3
+    {"servers": [ {"host": "@MASTER_NODE_IP@"} ],
4
+        "watchfiles": [
5
+            {"tag": "bootstrap/dmesg", "files": ["/var/log/dmesg"]},
6
+            {"tag": "bootstrap/secure", "files": ["/var/log/secure"]},
7
+            {"tag": "bootstrap/messages", "files": ["/var/log/messages"]},
8
+            {"tag": "bootstrap/fuel-agent", "files": ["/var/log/fuel-agent.log"]},
9
+            {"tag": "bootstrap/mcollective", "log_type": "ruby",
10
+                "files": ["/var/log/mcollective.log"]},
11
+            {"tag": "bootstrap/agent", "log_type": "ruby",
12
+                "files": ["/var/log/nailgun-agent.log"]},
13
+            {"tag": "bootstrap/netprobe_sender", "log_type": "netprobe",
14
+                "files": ["/var/log/netprobe_sender.log"]},
15
+            {"tag": "bootstrap/netprobe_listener", "log_type": "netprobe",
16
+                "files": ["/var/log/netprobe_listener.log"]}
17
+        ]
18
+    }
19
+]
20
+}

+ 20
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/etc/ssh/sshd_config View File

@@ -0,0 +1,20 @@
1
+Protocol 2
2
+SyslogFacility AUTHPRIV
3
+PasswordAuthentication no
4
+PubkeyAuthentication yes
5
+ChallengeResponseAuthentication no
6
+GSSAPIAuthentication no
7
+UsePAM no
8
+UseDNS no
9
+
10
+# Accept locale-related environment variables
11
+AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
12
+AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
13
+AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
14
+AcceptEnv XMODIFIERS
15
+
16
+Subsystem	sftp	/usr/lib/openssh/sftp-server
17
+
18
+# Secure Ciphers and MACs
19
+Ciphers aes256-ctr,aes192-ctr,aes128-ctr,arcfour256,arcfour128
20
+MACs hmac-sha2-512,hmac-sha2-256,hmac-ripemd160,hmac-sha1

+ 1
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/root/.ssh/authorized_keys View File

@@ -0,0 +1 @@
1
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDtrVTSM8tGd4E8khJn2gfN/2fymnX/0YKAGSVZTWDNIcYL5zXTlSwrccn/8EgmnNsJNxucJRT+oWqrDGaFaehuwlY/IBqm50KJVaUr5QYzOUpqVpFIpoX3UwETCxcSB1LiQYbCvrJcqOPQ4Zu9fMhMGKaAX1ohzOumn4czuLDYIvCnPnoU5RDWt7g1GaFFlzGU3JFooj7/aWFJMqJLinvay3vr2vFpBvO1y29nKu+zgpZkzzJCc0ndoVqvB+W9DY6QtgTSWfd3ZE/8vg4h8QV8H+xxqL/uWCxDkv2Y3rviAHivR/V+1YCSQH0NBJrNSkRjd+1roLhcEGT7/YEnbgVV nailgun@bootstrap

+ 30
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/usr/bin/fix-configs-on-startup View File

@@ -0,0 +1,30 @@
1
+#!/bin/sh
2
+
3
+masternode_ip=`sed -rn 's/^.*url=http:\/\/(([0-9]{1,3}\.){3}[0-9]{1,3}).*$/\1/ p' /proc/cmdline`
4
+mco_user=$(sed 's/\ /\n/g' /proc/cmdline | grep mco_user | awk -F\= '{print $2}')
5
+mco_pass=$(sed 's/\ /\n/g' /proc/cmdline | grep mco_pass | awk -F\= '{print $2}')
6
+[ -z "$mco_user" ] && mco_user="mcollective"
7
+[ -z "$mco_pass" ] && mco_pass="marionette"
8
+
9
+# Send logs to master node.
10
+sed -i /etc/send2syslog.conf -re "s/@MASTER_NODE_IP@/$masternode_ip/"
11
+
12
+/usr/bin/send2syslog.py -i < /etc/send2syslog.conf
13
+
14
+# Set up NTP
15
+# Disable panic about huge clock offset
16
+sed -i '/^\s*tinker panic/ d' /etc/ntp.conf
17
+sed -i '1 i tinker panic 0' /etc/ntp.conf
18
+
19
+# Sync clock with master node
20
+sed -i "/^\s*server\b/ d" /etc/ntp.conf
21
+echo "server $masternode_ip burst iburst" >> /etc/ntp.conf
22
+
23
+service ntp restart
24
+
25
+# Update mcollective config
26
+sed -i "s/^plugin.rabbitmq.pool.1.host\b.*$/plugin.rabbitmq.pool.1.host = $masternode_ip/" /etc/mcollective/server.cfg
27
+sed -i "s/^plugin.rabbitmq.pool.1.user\b.*$/plugin.rabbitmq.pool.1.user = $mco_user/" /etc/mcollective/server.cfg
28
+sed -i "s/^plugin.rabbitmq.pool.1.password\b.*$/plugin.rabbitmq.pool.1.password= $mco_pass/" /etc/mcollective/server.cfg
29
+
30
+service mcollective restart

+ 505
- 0
deployment_scripts/fuel-bootstrap-image-builder/share/fuel-bootstrap-image/ubuntu/files/usr/bin/send2syslog.py View File

@@ -0,0 +1,505 @@
1
+#!/usr/bin/env python
2
+
3
+#    Copyright 2013 Mirantis, Inc.
4
+#
5
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+#    not use this file except in compliance with the License. You may obtain
7
+#    a copy of the License at
8
+#
9
+#         http://www.apache.org/licenses/LICENSE-2.0
10
+#
11
+#    Unless required by applicable law or agreed to in writing, software
12
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+#    License for the specific language governing permissions and limitations
15
+#    under the License.
16
+
17
+import json
18
+import logging
19
+from logging.handlers import SysLogHandler
20
+from optparse import OptionParser
21
+import os
22
+import re
23
+import signal
24
+import sys
25
+import time
26
+
27
+
28
+# Add syslog levels to logging module.
29
+logging.NOTICE = 25
30
+logging.ALERT = 60
31
+logging.EMERG = 70
32
+logging.addLevelName(logging.NOTICE, 'NOTICE')
33
+logging.addLevelName(logging.ALERT, 'ALERT')
34
+logging.addLevelName(logging.EMERG, 'EMERG')
35
+SysLogHandler.priority_map['NOTICE'] = 'notice'
36
+SysLogHandler.priority_map['ALERT'] = 'alert'
37
+SysLogHandler.priority_map['EMERG'] = 'emerg'
38
+# Define data and message format according to RFC 5424.
39
+rfc5424_format = '{version} {timestamp} {hostname} {appname} {procid}'\
40
+                 ' {msgid} {structured_data} {msg}'
41
+date_format = '%Y-%m-%dT%H:%M:%SZ'
42
+# Define global semaphore.
43
+sending_in_progress = 0
44
+# Define file types.
45
+msg_levels = {'ruby': {'regex': '(?P<level>[DIWEF]), \[[0-9-]{10}T',
46
+                       'levels': {'D': logging.DEBUG,
47
+                                  'I': logging.INFO,
48
+                                  'W': logging.WARNING,
49
+                                  'E': logging.ERROR,
50
+                                  'F': logging.FATAL
51
+                                  }
52
+                       },
53
+              'syslog': {'regex': ('[0-9-]{10}T[0-9:]{8}Z (?P<level>'
54
+                                   'debug|info|notice|warning|err|crit|'
55
+                                   'alert|emerg)'),
56
+                         'levels': {'debug': logging.DEBUG,
57
+                                    'info': logging.INFO,
58
+                                    'notice': logging.NOTICE,
59
+                                    'warning': logging.WARNING,
60
+                                    'err': logging.ERROR,
61
+                                    'crit': logging.CRITICAL,
62
+                                    'alert': logging.ALERT,
63
+                                    'emerg': logging.EMERG
64
+                                    }
65
+                         },
66
+              'anaconda': {'regex': ('[0-9:]{8},[0-9]+ (?P<level>'
67
+                                     'DEBUG|INFO|WARNING|ERROR|CRITICAL)'),
68
+                           'levels': {'DEBUG': logging.DEBUG,
69
+                                      'INFO': logging.INFO,
70
+                                      'WARNING': logging.WARNING,
71
+                                      'ERROR': logging.ERROR,
72
+                                      'CRITICAL': logging.CRITICAL
73
+                                      }
74
+                           },
75
+              'netprobe': {'regex': ('[0-9-]{10} [0-9:]{8},[0-9]+ (?P<level>'
76
+                                     'DEBUG|INFO|WARNING|ERROR|CRITICAL)'),
77
+                           'levels': {'DEBUG': logging.DEBUG,
78
+                                      'INFO': logging.INFO,
79
+                                      'WARNING': logging.WARNING,
80
+                                      'ERROR': logging.ERROR,
81
+                                      'CRITICAL': logging.CRITICAL
82
+                                      }
83
+                           }
84
+
85
+              }
86
+relevel_errors = {
87
+    'anaconda': [
88
+        {
89
+            'regex': 'Error downloading \
90
+http://.*/images/(product|updates).img: HTTP response code said error',
91
+            'levelfrom': logging.ERROR,
92
+            'levelto': logging.WARNING
93
+        },
94
+        {
95
+            'regex': 'got to setupCdrom without a CD device',
96
+            'levelfrom': logging.ERROR,
97
+            'levelto': logging.WARNING
98
+        }
99
+    ]
100
+}
101
+# Create a main logger.
102
+logging.basicConfig(format='%(levelname)s: %(message)s')
103
+main_logger = logging.getLogger()
104
+main_logger.setLevel(logging.NOTSET)
105
+
106
+
107
+class WatchedFile:
108
+    """WatchedFile(filename) => Object that read lines from file if exist."""
109
+
110
+    def __init__(self, name):
111
+        self.name = name
112
+        self.fo = None
113
+        self.where = 0
114
+
115
+    def reset(self):
116
+        if self.fo:
117
+            self.fo.close()
118
+            self.fo = None
119
+            self.where = 0
120
+
121
+    def _checkRewrite(self):
122
+        try:
123
+            if os.stat(self.name)[6] < self.where:
124
+                self.reset()
125
+        except OSError:
126
+            self.close()
127
+
128
+    def readLines(self):
129
+        """Return list of last append lines from file if exist."""
130
+
131
+        self._checkRewrite()
132
+        if not self.fo:
133
+            try:
134
+                self.fo = open(self.name, 'r')
135
+            except IOError:
136
+                return ()
137
+        lines = self.fo.readlines()
138
+        self.where = self.fo.tell()
139
+        return lines
140
+
141
+    def close(self):
142
+        self.reset()
143
+
144
+
145
+class WatchedGroup:
146
+    """Can send data from group of specified files to specified servers."""
147
+
148
+    def __init__(self, servers, files, name):
149
+        self.servers = servers
150
+        self.files = files
151
+        self.log_type = files.get('log_type', 'syslog')
152
+        self.name = name
153
+        self._createLogger()
154
+
155
+    def _createLogger(self):
156
+        self.watchedfiles = []
157
+        logger = logging.getLogger(self.name)
158
+        logger.setLevel(logging.NOTSET)
159
+        logger.propagate = False
160
+        # Create log formatter.
161
+        format_dict = {'version': '1',
162
+                       'timestamp': '%(asctime)s',
163
+                       'hostname': config['hostname'],
164
+                       'appname': self.files['tag'],
165
+                       'procid': '-',
166
+                       'msgid': '-',
167
+                       'structured_data': '-',
168
+                       'msg': '%(message)s'
169
+                       }
170
+        log_format = rfc5424_format.format(**format_dict)
171
+        formatter = logging.Formatter(log_format, date_format)
172
+        # Add log handler for each server.
173
+        for server in self.servers:
174
+            port = 'port' in server and server['port'] or 514
175
+            syslog = SysLogHandler((server["host"], port))
176
+            syslog.setFormatter(formatter)
177
+            logger.addHandler(syslog)
178
+        self.logger = logger
179
+        # Create WatchedFile objects from list of files.
180
+        for name in self.files['files']:
181
+            self.watchedfiles.append(WatchedFile(name))
182
+
183
+    def send(self):
184
+        """Send append data from files to servers."""
185
+
186
+        for watchedfile in self.watchedfiles:
187
+            for line in watchedfile.readLines():
188
+                line = line.strip()
189
+                level = self._get_msg_level(line, self.log_type)
190
+                # Get rid of duplicated information in anaconda logs
191
+                line = re.sub(
192
+                    msg_levels[self.log_type]['regex'] + "\s*:?\s?",
193
+                    "",
194
+                    line
195
+                )
196
+                # Ignore meaningless errors
197
+                try:
198
+                    for r in relevel_errors[self.log_type]:
199
+                        if level == r['levelfrom'] and \
200
+                                re.match(r['regex'], line):
201
+                            level = r['levelto']
202
+                except KeyError:
203
+                    pass
204
+                self.logger.log(level, line)
205
+                main_logger and main_logger.log(
206
+                    level,
207
+                    'From file "%s" send: %s' % (watchedfile.name, line)
208
+                )
209
+
210
+    @staticmethod
211
+    def _get_msg_level(line, log_type):
212
+        if log_type in msg_levels:
213
+            msg_type = msg_levels[log_type]
214
+            regex = re.match(msg_type['regex'], line)
215
+            if regex:
216
+                return msg_type['levels'][regex.group('level')]
217
+        return logging.INFO
218
+
219
+
220
+def sig_handler(signum, frame):
221
+    """Send all new data when signal arrived."""
222
+
223
+    if not sending_in_progress:
224
+        send_all()
225
+        exit(signum)
226
+    else:
227
+        config['run_once'] = True
228
+
229
+
230
+def send_all():
231
+    """Send any updates."""
232
+
233
+    for group in watchlist:
234
+        group.send()
235
+
236
+
237
+def main_loop():
238
+    """Periodicaly call sendlogs() for each group in watchlist."""
239
+
240
+    signal.signal(signal.SIGINT, sig_handler)
241
+    signal.signal(signal.SIGTERM, sig_handler)
242
+    while watchlist:
243
+        time.sleep(0.5)
244
+        send_all()
245
+        # If asked to run_once, exit now
246
+        if config['run_once']:
247
+            break
248
+
249
+
250
+class Config:
251
+    """Collection of config generation methods.
252
+    Usage: config = Config.getConfig()
253
+    """
254
+
255
+    @classmethod
256
+    def getConfig(cls):
257
+        """Generate config from command line arguments and config file."""
258
+
259
+        # example_config = {
260
+        #       "daemon": True,
261
+        #       "run_once": False,
262
+        #       "debug": False,
263
+        #       "watchlist": [
264
+        #           {"servers": [ {"host": "localhost", "port": 514} ],
265
+        #            "watchfiles": [
266
+        #               {"tag": "anaconda",
267
+        #                "log_type": "anaconda",
268
+        #                "files": ["/tmp/anaconda.log",
269
+        #                   "/mnt/sysimage/root/install.log"]
270
+        #                }
271
+        #               ]
272
+        #            }
273
+        #           ]
274
+        #       }
275
+
276
+        default_config = {"daemon": True,
277
+                          "run_once": False,
278
+                          "debug": False,
279
+                          "hostname": cls._getHostname(),
280
+                          "watchlist": []
281
+                          }
282
+        # First use default config as running config.
283
+        config = dict(default_config)
284
+        # Get command line options and validate it.
285
+        cmdline = cls.cmdlineParse()[0]
286
+        # Check config file source and read it.
287
+        if cmdline.config_file or cmdline.stdin_config:
288
+            try:
289
+                if cmdline.stdin_config is True:
290
+                    fo = sys.stdin
291
+                else:
292
+                    fo = open(cmdline.config_file, 'r')
293
+                parsed_config = json.load(fo)
294
+                if cmdline.debug:
295
+                    print(parsed_config)
296
+            except IOError:  # Raised if IO operations failed.
297
+                main_logger.error("Can not read config file %s\n" %
298
+                                  cmdline.config_file)
299
+                exit(1)
300
+            except ValueError as e:  # Raised if json parsing failed.
301
+                main_logger.error("Can not parse config file. %s\n" %
302
+                                  e.message)
303
+                exit(1)
304
+            #  Validate config from config file.
305
+            cls.configValidate(parsed_config)
306
+            # Copy gathered config from config file to running config
307
+            # structure.
308
+            for key, value in parsed_config.items():
309
+                config[key] = value
310
+        else:
311
+            # If no config file specified use watchlist setting from
312
+            # command line.
313
+            watchlist = {"servers": [{"host": cmdline.host,
314
+                                      "port": cmdline.port}],
315
+                         "watchfiles": [{"tag": cmdline.tag,
316
+                                         "log_type": cmdline.log_type,
317
+                                         "files": cmdline.watchfiles}]}
318
+            config['watchlist'].append(watchlist)
319
+
320
+        # Apply behavioural command line options to running config.
321
+        if cmdline.no_daemon:
322
+            config["daemon"] = False
323
+        if cmdline.run_once:
324
+            config["run_once"] = True
325
+        if cmdline.debug:
326
+            config["debug"] = True
327
+        return config
328
+
329
+    @staticmethod
330
+    def _getHostname():
331
+        """Generate hostname by BOOTIF kernel option or use os.uname()."""
332
+
333
+        with open('/proc/cmdline') as fo:
334
+            cpu_cmdline = fo.readline().strip()
335
+        regex = re.search('(?<=BOOTIF=)([0-9a-fA-F-]*)', cpu_cmdline)
336
+        if regex:
337
+            mac = regex.group(0).upper()
338
+            return ''.join(mac.split('-'))
339
+        return os.uname()[1]
340
+
341
+    @staticmethod
342
+    def cmdlineParse():
343
+        """Parse command line config options."""
344
+
345
+        parser = OptionParser()
346
+        parser.add_option("-c", "--config", dest="config_file", metavar="FILE",
347
+                          help="Read config from FILE.")
348
+        parser.add_option("-i", "--stdin", dest="stdin_config", default=False,
349
+                          action="store_true", help="Read config from Stdin.")
350
+        # FIXIT Add optionGroups.
351
+        parser.add_option("-r", "--run-once", dest="run_once",
352
+                          action="store_true", help="Send all data and exit.")
353
+        parser.add_option("-n", "--no-daemon", dest="no_daemon",
354
+                          action="store_true", help="Do not daemonize.")
355
+        parser.add_option("-d", "--debug", dest="debug",
356
+                          action="store_true", help="Print debug messages.")
357
+
358
+        parser.add_option("-t", "--tag", dest="tag", metavar="TAG",
359
+                          help="Set tag of sending messages as TAG.")
360
+        parser.add_option("-T", "--type", dest="log_type", metavar="TYPE",
361
+                          default='syslog',
362
+                          help="Set type of files as TYPE"
363
+                               "(default: %default).")
364
+        parser.add_option("-f", "--watchfile", dest="watchfiles",
365
+                          action="append",
366
+                          metavar="FILE", help="Add FILE to watchlist.")
367
+        parser.add_option("-s", "--host", dest="host", metavar="HOSTNAME",
368
+                          help="Set destination as HOSTNAME.")
369
+        parser.add_option("-p", "--port", dest="port", type="int", default=514,
370
+                          metavar="PORT",
371
+                          help="Set remote port as PORT (default: %default).")
372
+
373
+        options, args = parser.parse_args()
374
+        # Validate gathered options.
375
+        if options.config_file and options.stdin_config:
376
+            parser.error("You must not set both options --config"
377
+                         " and --stdin at the same time.")
378
+            exit(1)
379
+        if ((options.config_file or options.stdin_config) and
380
+                (options.tag or options.watchfiles or options.host)):
381
+            main_logger.warning("If --config or --stdin is set up options"
382
+                                " --tag, --watchfile, --type,"
383
+                                " --host and --port will be ignored.")
384
+        if (not (options.config_file or options.stdin_config) and
385
+                not (options.tag and options.watchfiles and options.host)):
386
+            parser.error("Options --tag, --watchfile and --host"
387
+                         " must be set up at the same time.")
388
+            exit(1)
389
+        return options, args
390
+
391
+    @staticmethod
392
+    def _checkType(value, value_type, value_name='', msg=None):
393
+        """Check correctness of type of value and exit if not."""
394
+
395
+        if not isinstance(value, value_type):
396
+            message = msg or "Value %r in config have type %r but"\
397
+                " %r is expected." %\
398
+                (value_name, type(value).__name__, value_type.__name__)
399
+            main_logger.error(message)
400
+            exit(1)
401
+
402
+    @classmethod
403
+    def configValidate(cls, config):
404
+        """Validate types and names of data items in config."""
405
+
406
+        cls._checkType(config, dict, msg='Config must be a dict.')
407
+        for key in ("daemon", "run_once", "debug"):
408
+            if key in config:
409
+                cls._checkType(config[key], bool, key)
410
+        key = "hostname"
411
+        if key in config:
412
+            cls._checkType(config[key], basestring, key)
413
+
414
+        key = "watchlist"
415
+        if key in config:
416
+            cls._checkType(config[key], list, key)
417
+        else:
418
+            main_logger.error("There must be key %r in config." % key)
419
+            exit(1)
420
+
421
+        for item in config["watchlist"]:
422
+            cls._checkType(item, dict, "watchlist[n]")
423
+            key, name = "servers", "watchlist[n]  => servers"
424
+            if key in item:
425
+                cls._checkType(item[key], list, name)
426
+            else:
427
+                main_logger.error("There must be key %r in %s in config." %
428
+                                  (key, '"watchlist[n]" item'))
429
+                exit(1)
430
+            key, name = "watchfiles", "watchlist[n] => watchfiles"
431
+            if key in item:
432
+                cls._checkType(item[key], list, name)
433
+            else:
434
+                main_logger.error("There must be key %r in %s in config." %
435
+                                  (key, '"watchlist[n]" item'))
436
+                exit(1)
437
+
438
+            for item2 in item["servers"]:
439
+                cls._checkType(item2, dict, "watchlist[n]  => servers[n]")
440
+                key, name = "host", "watchlist[n]  => servers[n] => host"
441
+                if key in item2:
442
+                    cls._checkType(item2[key], basestring, name)
443
+                else:
444
+                    main_logger.error("There must be key %r in %s in config." %
445
+                                      (key,
446
+                                       '"watchlist[n] => servers[n]" item'))
447
+                    exit(1)
448
+                key, name = "port", "watchlist[n]  => servers[n] => port"
449
+                if key in item2:
450
+                    cls._checkType(item2[key], int, name)
451
+
452
+            for item2 in item["watchfiles"]:
453
+                cls._checkType(item2, dict, "watchlist[n]  => watchfiles[n]")
454
+                key, name = "tag", "watchlist[n]  => watchfiles[n] => tag"
455
+                if key in item2:
456
+                    cls._checkType(item2[key], basestring, name)
457
+                else:
458
+                    main_logger.error("There must be key %r in %s in config." %
459
+                                      (key,
460
+                                       '"watchlist[n] => watchfiles[n]" item'))
461
+                    exit(1)
462
+                key = "log_type"
463
+                name = "watchlist[n]  => watchfiles[n] => log_type"
464
+                if key in item2:
465
+                    cls._checkType(item2[key], basestring, name)
466
+                key, name = "files", "watchlist[n]  => watchfiles[n] => files"
467
+                if key in item2:
468
+                    cls._checkType(item2[key], list, name)
469
+                else:
470
+                    main_logger.error("There must be key %r in %s in config." %
471
+                                      (key,
472
+                                       '"watchlist[n] => watchfiles[n]" item'))
473
+                    exit(1)
474
+                for item3 in item2["files"]:
475
+                    name = "watchlist[n]  => watchfiles[n] => files[n]"
476
+                    cls._checkType(item3, basestring, name)
477
+
478
+
479
+# Create global config.
480
+config = Config.getConfig()
481
+# Create list of WatchedGroup objects with different log names.
482
+watchlist = []
483
+i = 0
484
+for item in config["watchlist"]:
485
+    for files in item['watchfiles']:
486
+        watchlist.append(WatchedGroup(item['servers'], files, str(i)))
487
+        i = i + 1
488
+
489
+# Fork and loop
490
+if config["daemon"]:
491
+    if not os.fork():
492
+        # Redirect the standard I/O file descriptors to the specified file.
493
+        main_logger = None
494
+        DEVNULL = getattr(os, "devnull", "/dev/null")
495
+        os.open(DEVNULL, os.O_RDWR)  # standard input (0)
496
+        os.dup2(0, 1)  # Duplicate standard input to standard output (1)
497
+        os.dup2(0, 2)  # Duplicate standard input to standard error (2)
498
+
499
+        main_loop()
500
+        sys.exit(1)
501
+    sys.exit(0)
502
+else:
503
+    if not config['debug']:
504
+        main_logger = None
505
+    main_loop()

+ 38
- 0
deployment_scripts/post_swift_key.rb View File

@@ -0,0 +1,38 @@
1
+#!/usr/bin/env ruby
2
+require 'hiera'
3
+
4
+ENV['LANG'] = 'C'
5
+
6
+hiera = Hiera.new(:config => '/etc/hiera.yaml')
7
+glanced = hiera.lookup 'glance', {} , {}
8
+management_vip = hiera.lookup 'management_vip', nil, {}
9
+auth_addr = hiera.lookup 'service_endpoint', "#{management_vip}", {}
10
+tenant_name = glanced['tenant'].nil? ? "services" : glanced['tenant']
11
+user_name = glanced['user'].nil? ? "glance" : glanced['user']
12
+endpoint_type = glanced['endpoint_type'].nil? ? "internalURL" : glanced['endpoint_type']
13
+region_name = hiera.lookup 'region', 'RegionOne', {}
14
+ironic_hash = hiera.lookup 'fuel-plugin-ironic', {}, {}
15
+ironic_swift_tempurl_key = ironic_hash['password'].nil? ? "ironic" : ironic_hash['password']
16
+
17
+ENV['OS_TENANT_NAME']="#{tenant_name}"
18
+ENV['OS_USERNAME']="#{user_name}"
19
+ENV['OS_PASSWORD']="#{glanced['user_password']}"
20
+ENV['OS_AUTH_URL']="http://#{auth_addr}:5000/v2.0"
21
+ENV['OS_ENDPOINT_TYPE'] = "#{endpoint_type}"
22
+ENV['OS_REGION_NAME']="#{region_name}"
23
+
24
+
25
+command = <<-EOF
26
+/usr/bin/swift post -m 'Temp-URL-Key:#{ironic_swift_tempurl_key}'
27
+EOF
28
+puts command
29
+5.times.each do |retries|
30
+  sleep 10 if retries > 0
31
+  stdout = `#{command}`
32
+  return_code = $?.exitstatus
33
+  puts stdout
34
+  exit 0 if return_code == 0
35
+end
36
+
37
+puts "Secret key registration have FAILED!"
38
+exit 1

+ 51
- 0
deployment_scripts/puppet/manifests/db.pp View File

@@ -0,0 +1,51 @@
1
+notice('MODULAR: ironic/db.pp')
2
+
3
+$node_name = hiera('node_name')
4
+
5
+$ironic_hash    = hiera_hash('fuel-plugin-ironic', {})
6
+$mysql_hash     = hiera_hash('mysql', {})
7
+
8
+$mysql_root_user     = pick($mysql_hash['root_user'], 'root')
9
+$mysql_db_create     = pick($mysql_hash['db_create'], true)
10
+$mysql_root_password = $mysql_hash['root_password']
11
+
12
+$db_user     = pick($ironic_hash['db_user'], 'ironic')
13
+$db_name     = pick($ironic_hash['db_name'], 'ironic')
14
+$db_password = pick($ironic_hash['password'], 'ironic')
15
+
16
+$db_host          = pick($ironic_hash['db_host'], $database_vip, 'localhost')
17
+$db_create        = pick($ironic_hash['db_create'], $mysql_db_create)
18
+$db_root_user     = pick($ironic_hash['root_user'], $mysql_root_user)
19
+$db_root_password = pick($ironic_hash['root_password'], $mysql_root_password)
20
+
21
+$allowed_hosts = [ $node_name, 'localhost', '127.0.0.1', '%' ]
22
+
23
+if $ironic_hash['metadata']['enabled'] and $db_create {
24
+
25
+  class { 'galera::client':
26
+    custom_setup_class => hiera('mysql_custom_setup_class', 'galera'),
27
+  }
28
+
29
+  class { 'ironic::db::mysql':
30
+    user          => $db_user,
31
+    password      => $db_password,
32
+    dbname        => $db_name,
33
+    allowed_hosts => $allowed_hosts,
34
+  }
35
+
36
+  class { 'osnailyfacter::mysql_access':
37
+    db_host     => $db_host,
38
+    db_user     => $db_root_user,
39
+    db_password => $db_root_password,
40
+  }
41
+
42
+  Class['galera::client'] ->
43
+    Class['osnailyfacter::mysql_access'] ->
44
+      Class['ironic::db::mysql']
45
+
46
+}
47
+
48
+class mysql::config {}
49
+include mysql::config
50
+class mysql::server {}
51
+include mysql::server

+ 58
- 0
deployment_scripts/puppet/manifests/haproxy.pp View File

@@ -0,0 +1,58 @@
1
+notice('MODULAR: ironic/haproxy.pp')
2
+
3
+$network_metadata   = hiera_hash('network_metadata')
4
+$public_ssl_hash    = hiera('public_ssl')
5
+
6
+$ironic_api_nodes   = get_nodes_hash_by_roles($network_metadata, ['primary-controller', 'controller'])
7
+$ironic_address_map = get_node_to_ipaddr_map_by_network_role($ironic_api_nodes, 'ironic/api')
8
+$ironic_server_names = hiera_array('ironic_names', keys($ironic_address_map))
9
+$ironic_ipaddresses = hiera_array('ironic_ipaddresses', values($ironic_address_map))
10
+
11
+$swift_proxies_address_map = get_node_to_ipaddr_map_by_network_role(hiera_hash('swift_proxies', undef), 'swift/api')
12
+$swift_server_names        = hiera_array('swift_server_names', keys($swift_proxies_address_map))
13
+$swift_ipaddresses         = hiera_array('swift_ipaddresses', values($swift_proxies_address_map))
14
+
15
+$public_virtual_ip    = hiera('public_vip')
16
+$internal_virtual_ip  = hiera('management_vip')
17
+$baremetal_virtual_ip = $network_metadata['vips']['baremetal']['ipaddr']
18
+
19
+Openstack::Ha::Haproxy_service {
20
+  ipaddresses            => $ironic_ipaddresses,
21
+  public_virtual_ip      => $public_virtual_ip,
22
+  server_names           => $ironic_server_names,
23
+  public                 => true,
24
+  public_ssl             => $public_ssl_hash['services'],
25
+  haproxy_config_options => {
26
+    option => ['httpchk GET /', 'httplog','httpclose'],
27
+  },
28
+}
29
+
30
+openstack::ha::haproxy_service { 'ironic-api':
31
+  order               => '180',
32
+  listen_port         => 6385,
33
+  internal_virtual_ip => $internal_virtual_ip,
34
+}
35
+
36
+openstack::ha::haproxy_service { 'ironic-baremetal':
37
+  order               => '185',
38
+  listen_port         => 6385,
39
+  public              => false,
40
+  public_ssl          => false,
41
+  public_virtual_ip   => false,
42
+  internal_virtual_ip => $baremetal_virtual_ip,
43
+}
44
+
45
+openstack::ha::haproxy_service { 'swift-baremetal':
46
+  order                  => '125',
47
+  listen_port            => 8080,
48
+  ipaddresses            => $swift_ipaddresses,
49
+  server_names           => $swift_server_names,
50
+  public                 => false,
51
+  public_ssl             => false,
52
+  public_virtual_ip      => false,
53
+  internal_virtual_ip    => $baremetal_virtual_ip,
54
+  haproxy_config_options => {
55
+    'option' => ['httpchk', 'httplog', 'httpclose'],
56
+  },
57
+  balancermember_options => 'check port 49001 inter 15s fastinter 2s downinter 8s rise 3 fall 3',
58
+}

+ 91
- 0
deployment_scripts/puppet/manifests/ironic-compute.pp View File

@@ -0,0 +1,91 @@
1
+notice('MODULAR: ironic/ironic-compute.pp')
2
+
3
+$ironic_hash                    = hiera_hash('fuel-plugin-ironic', {})
4
+$nova_hash                      = hiera_hash('nova', {})
5
+$management_vip                 = hiera('management_vip')
6
+$database_vip                   = hiera('database_vip', $management_vip)
7
+$keystone_endpoint              = hiera('keystone_endpoint', $management_vip)
8
+$neutron_endpoint               = hiera('neutron_endpoint', $management_vip)
9
+$ironic_endpoint                = hiera('ironic_endpoint', $management_vip)
10
+$glance_api_servers             = hiera('glance_api_servers', "${management_vip}:9292")
11
+$debug                          = hiera('debug', false)
12
+$verbose                        = hiera('verbose', true)
13
+$use_syslog                     = hiera('use_syslog', true)
14
+$syslog_log_facility_ironic     = hiera('syslog_log_facility_ironic', 'LOG_LOCAL0')
15
+$syslog_log_facility_nova       = hiera('syslog_log_facility_nova', 'LOG_LOCAL6')
16
+$amqp_hosts                     = hiera('amqp_hosts')
17
+$rabbit_hash                    = hiera('rabbit_hash')
18
+$nova_report_interval           = hiera('nova_report_interval')
19
+$nova_service_down_time         = hiera('nova_service_down_time')
20
+$neutron_config                 = hiera_hash('quantum_settings')
21
+
22
+$ironic_tenant                  = pick($ironic_hash['tenant'],'services')
23
+$ironic_user                    = pick($ironic_hash['user'],'ironic')
24
+$ironic_user_password           = pick($ironic_hash['password'],'ironic')
25
+
26
+$db_host                        = pick($nova_hash['db_host'], $database_vip)
27
+$db_user                        = pick($nova_hash['db_user'], 'nova')
28
+$db_name                        = pick($nova_hash['db_name'], 'nova')
29
+$db_password                    = pick($nova_hash['db_password'], 'nova')
30
+$database_connection            = "mysql://${db_name}:${db_password}@${db_host}/${db_name}?read_timeout=60"
31
+
32
+$memcache_nodes                 = get_nodes_hash_by_roles(hiera('network_metadata'), hiera('memcache_roles'))
33
+$cache_server_ip                = ipsort(values(get_node_to_ipaddr_map_by_network_role($memcache_nodes,'mgmt/memcache')))
34
+$memcached_addresses            = suffix($cache_server_ip, inline_template(":<%= @cache_server_port %>"))
35
+$notify_on_state_change         = 'vm_and_task_state'
36
+
37
+
38
+class { '::nova':
39
+    install_utilities      => false,
40
+    ensure_package         => installed,
41
+    database_connection    => $database_connection,
42
+    rpc_backend            => 'nova.openstack.common.rpc.impl_kombu',
43
+    #FIXME(bogdando) we have to split amqp_hosts until all modules synced
44
+    rabbit_hosts           => split($amqp_hosts, ','),
45
+    rabbit_userid          => $rabbit_hash['user'],
46
+    rabbit_password        => $rabbit_hash['password'],
47
+    image_service          => 'nova.image.glance.GlanceImageService',
48
+    glance_api_servers     => $glance_api_servers,
49
+    verbose                => $verbose,
50
+    debug                  => $debug,
51
+    use_syslog             => $use_syslog,
52
+    log_facility           => $syslog_log_facility_nova,
53
+    state_path             => $nova_hash['state_path'],
54
+    report_interval        => $nova_report_interval,
55
+    service_down_time      => $nova_service_down_time,
56
+    notify_on_state_change => $notify_on_state_change,
57
+    memcached_servers      => $memcached_addresses,
58
+}
59
+
60
+
61
+class { '::nova::compute':
62
+  ensure_package            => installed,
63
+  enabled                   => true,
64
+  vnc_enabled               => false,
65
+  force_config_drive        => $nova_hash['force_config_drive'],
66
+  #NOTE(bogdando) default became true in 4.0.0 puppet-nova (was false)
67
+  neutron_enabled           => true,
68
+  default_availability_zone => $nova_hash['default_availability_zone'],
69
+  default_schedule_zone     => $nova_hash['default_schedule_zone'],
70
+  reserved_host_memory      => '0',
71
+}
72
+
73
+
74
+class { 'nova::compute::ironic':
75
+  admin_url         => "http://${keystone_endpoint}:35357/v2.0",
76
+  admin_user        => $ironic_user,
77
+  admin_tenant_name => $ironic_tenant,
78
+  admin_passwd      => $ironic_user_password,
79
+  api_endpoint      => "http://${ironic_endpoint}:6385/v1",
80
+}
81
+
82
+class { 'nova::network::neutron':
83
+  neutron_admin_password => $neutron_config['keystone']['admin_password'],
84
+  neutron_url            => "http://${neutron_endpoint}:9696",
85
+  neutron_admin_auth_url => "http://${keystone_endpoint}:35357/v2.0",
86
+}
87
+
88
+file { '/etc/nova/nova-compute.conf':
89
+  ensure => absent,
90
+  require => Package['nova-compute'],
91
+} ~> Service['nova-compute']

+ 118
- 0
deployment_scripts/puppet/manifests/ironic-conductor.pp View File

@@ -0,0 +1,118 @@
1
+notice('MODULAR: ironic/ironic-conductor.pp')
2
+
3
+$network_scheme             = hiera('network_scheme', {})
4
+prepare_network_config($network_scheme)
5
+$baremetal_address          = get_network_role_property('ironic/baremetal', 'ipaddr')
6
+$ironic_hash                = hiera_hash('fuel-plugin-ironic', {})
7
+$management_vip             = hiera('management_vip')
8
+
9
+$network_metadata           = hiera_hash('network_metadata', {})
10
+$baremetal_vip              = $network_metadata['vips']['baremetal']['ipaddr']
11
+
12
+$database_vip               = hiera('database_vip', $management_vip)
13
+$keystone_endpoint          = hiera('keystone_endpoint', $management_vip)
14
+$neutron_endpoint           = hiera('neutron_endpoint', $management_vip)
15
+$glance_api_servers         = hiera('glance_api_servers', "${management_vip}:9292")
16
+$amqp_hosts                 = hiera('amqp_hosts')
17
+$rabbit_hosts               = split($amqp_hosts, ',')
18
+$debug                      = hiera('debug', false)
19
+$verbose                    = hiera('verbose', true)
20
+$use_syslog                 = hiera('use_syslog', true)
21
+$syslog_log_facility_ironic = hiera('syslog_log_facility_ironic', 'LOG_USER')
22
+$rabbit_hash                = hiera('rabbit_hash')
23
+$rabbit_ha_queues           = hiera('rabbit_ha_queues')
24
+
25
+$ironic_tenant              = pick($ironic_hash['tenant'],'services')
26
+$ironic_user                = pick($ironic_hash['user'],'ironic')
27
+$ironic_user_password       = pick($ironic_hash['password'],'ironic')
28
+$ironic_swift_tempurl_key   = pick($ironic_hash['password'],'ironic')
29
+
30
+$db_host                    = pick($ironic_hash['db_host'], $database_vip)
31
+$db_user                    = pick($ironic_hash['db_user'], 'ironic')
32
+$db_name                    = pick($ironic_hash['db_name'], 'ironic')
33
+$db_password                = pick($ironic_hash['password'], 'ironic')
34
+$database_connection        = "mysql://${db_name}:${db_password}@${db_host}/${db_name}?charset=utf8&read_timeout=60"
35
+
36
+$tftp_root                  = "/var/lib/ironic/tftpboot"
37
+
38
+class { '::ironic':
39
+  verbose                   => $verbose,
40
+  debug                     => $debug,
41
+  enabled_drivers           => ['fuel_ssh', 'fuel_ipmitool'],
42
+  rabbit_hosts              => $rabbit_hosts,
43
+  rabbit_port               => 5673,
44
+  rabbit_userid             => $rabbit_hash['user'],
45
+  rabbit_password           => $rabbit_hash['password'],
46
+  amqp_durable_queues       => $rabbit_ha_queues,
47
+  use_syslog                => $use_syslog,
48
+  log_facility              => $syslog_log_facility_ironic,
49
+  database_connection       => $database_connection,
50
+  glance_api_servers        => $glance_api_servers,
51
+}
52
+
53
+class { '::ironic::client': }
54
+
55
+class { '::ironic::conductor': }
56
+
57
+class { '::ironic::drivers::pxe':
58
+  tftp_server  => $baremetal_address,
59
+  tftp_root   => $tftp_root,
60
+  tftp_master_path => "${tftp_root}/master_images",
61
+}
62
+
63
+ironic_config {
64
+  'neutron/url':                          value => "http://${neutron_endpoint}:9696";
65
+  'keystone_authtoken/auth_uri':          value => "http://${keystone_endpoint}:5000/";
66
+  'keystone_authtoken/auth_host':         value => $keystone_endpoint;
67
+  'keystone_authtoken/admin_tenant_name': value => $ironic_tenant;
68
+  'keystone_authtoken/admin_user':        value => $ironic_user;
69
+  'keystone_authtoken/admin_password':    value => $ironic_user_password, secret => true;
70
+  'glance/swift_temp_url_key':            value => $ironic_swift_tempurl_key;
71
+  'glance/swift_endpoint_url':            value => "http://${baremetal_vip}:8080";
72
+  #'glance/swift_account':                value => "AUTH_${services_tenant_id}";
73
+  'conductor/api_url':                    value => "http://${baremetal_vip}:6385";
74
+}
75
+
76
+file { $tftp_root:
77
+  ensure  => directory,
78
+  owner   => 'ironic',
79
+  group   => 'ironic',
80
+  mode    => 755,
81
+  require => Class['ironic'],
82
+}
83
+
84
+file { "$tftp_root/pxelinux.0":
85
+  ensure  => present,
86
+  source  => '/usr/lib/syslinux/pxelinux.0',
87
+  require => Package['syslinux'],
88
+}
89
+
90
+file { "${tftp_root}/map-file":
91
+  content => "r ^([^/]) ${tftp_root}/\\1",
92
+}
93
+
94
+class { '::tftp':
95
+  directory => $tftp_root,
96
+  options   => "--map-file ${tftp_root}/map-file",
97
+  inetd     => false,
98
+  require   => File["${tftp_root}/map-file"],
99
+}
100
+
101
+package { 'syslinux':
102
+  ensure => 'present',
103
+}
104
+
105
+package { 'ipmitool':
106
+  ensure => 'present',
107
+  before => Class['::ironic::conductor'],
108
+}
109
+
110
+file { "/etc/ironic/fuel_key":
111
+  ensure  => present,
112
+  source  => '/var/lib/astute/ironic/bootstrap.rsa',
113
+  owner   => 'ironic',
114
+  group   => 'ironic',
115
+  mode    => 600,
116
+  require => Class['ironic'],
117
+}
118
+

+ 96
- 0
deployment_scripts/puppet/manifests/ironic.pp View File

@@ -0,0 +1,96 @@
1
+notice('MODULAR: ironic/ironic.pp')
2
+
3
+$ironic_hash                = hiera_hash('fuel-plugin-ironic', {})
4
+$nova_hash                  = hiera_hash('nova_hash', {})
5
+$access_hash                = hiera_hash('access',{})
6
+$public_vip                 = hiera('public_vip')
7
+$management_vip             = hiera('management_vip')
8
+
9
+$network_metadata           = hiera_hash('network_metadata', {})
10
+$baremetal_vip              = $network_metadata['vips']['baremetal']['ipaddr']
11
+
12
+$database_vip               = hiera('database_vip', $management_vip)
13
+$keystone_endpoint          = hiera('keystone_endpoint', $management_vip)
14
+$neutron_endpoint           = hiera('neutron_endpoint', $management_vip)
15
+$glance_api_servers         = hiera('glance_api_servers', "${management_vip}:9292")
16
+$debug                      = hiera('debug', false)
17
+$verbose                    = hiera('verbose', true)
18
+$use_syslog                 = hiera('use_syslog', true)
19
+$syslog_log_facility_ironic = hiera('syslog_log_facility_ironic', 'LOG_USER')
20
+$rabbit_hash                = hiera_hash('rabbit_hash', {})
21
+$rabbit_ha_queues           = hiera('rabbit_ha_queues')
22
+$amqp_hosts                 = hiera('amqp_hosts')
23
+$rabbit_hosts               = split($amqp_hosts, ',')
24
+$neutron_config             = hiera_hash('quantum_settings')
25
+
26
+$db_host                    = pick($ironic_hash['db_host'], $database_vip)
27
+$db_user                    = pick($ironic_hash['db_user'], 'ironic')
28
+$db_name                    = pick($ironic_hash['db_name'], 'ironic')
29
+$db_password                = pick($ironic_hash['password'], 'ironic')
30
+$database_connection        = "mysql://${db_name}:${db_password}@${db_host}/${db_name}?charset=utf8&read_timeout=60"
31
+
32
+$region                     = hiera('region', 'RegionOne')
33
+$public_url                 = "http://${public_vip}:6385"
34
+$admin_url                  = "http://${management_vip}:6385"
35
+$internal_url               = "http://${management_vip}:6385"
36
+
37
+$ironic_tenant              = pick($ironic_hash['tenant'],'services')
38
+$ironic_user                = pick($ironic_hash['user'],'ironic')
39
+$ironic_user_password       = pick($ironic_hash['password'],'ironic')
40
+
41
+prepare_network_config(hiera('network_scheme', {}))
42
+
43
+if $ironic_hash['metadata']['enabled'] {
44
+  class { 'ironic':
45
+    verbose             => $verbose,
46
+    debug               => $debug,
47
+    enabled_drivers     => ['fuel_ssh', 'fuel_ipmitool'],
48
+    rabbit_hosts        => $rabbit_hosts,
49
+    rabbit_port         => 5673,
50
+    rabbit_userid       => $rabbit_hash['user'],
51
+    rabbit_password     => $rabbit_hash['password'],
52
+    amqp_durable_queues => $rabbit_ha_queues,
53
+    use_syslog          => $use_syslog,
54
+    log_facility        => $syslog_log_facility_ironic,
55
+    database_connection => $database_connection,
56
+    glance_api_servers  => $glance_api_servers,
57
+  }
58
+
59
+  class { 'ironic::client': }
60
+
61
+  class { 'ironic::api':
62
+    host_ip           => get_network_role_property('ironic/api', 'ipaddr'),
63
+    auth_host         => $keystone_endpoint,
64
+    admin_tenant_name => $ironic_tenant,
65
+    admin_user        => $ironic_user,
66
+    admin_password    => $ironic_user_password,
67
+    neutron_url       => "http://${neutron_endpoint}:9696",
68
+  }
69
+
70
+  class { 'ironic::keystone::auth':
71
+    password     => $ironic_user_password,
72
+    region       => $region,
73
+    public_url   => $public_url,
74
+    internal_url => $internal_url,
75
+    admin_url    => $admin_url,
76
+  }
77
+
78
+  firewall { '207 ironic-api' :
79
+    dport   => '6385',
80
+    proto   => 'tcp',
81
+    action  => 'accept',
82
+  }
83
+
84
+  nova_config {
85
+    'DEFAULT/scheduler_host_manager':          value => 'nova.scheduler.ironic_host_manager.IronicHostManager';
86
+    'DEFAULT/scheduler_use_baremetal_filters': value => true;
87
+  }
88
+
89
+  include ::nova::params
90
+  service { 'nova-scheduler':
91
+    ensure => 'running',
92
+    name   => $::nova::params::scheduler_service_name,
93
+  }
94
+  Nova_config<| |> ~> Service['nova-scheduler']
95
+}
96
+

+ 54
- 0
deployment_scripts/puppet/manifests/network-conductor.pp View File

@@ -0,0 +1,54 @@
1
+notice('MODULAR: ironic/network-conductor.pp')
2
+
3
+$network_scheme    = hiera('network_scheme', {})
4
+prepare_network_config($network_scheme)
5
+$baremetal_int     = get_network_role_property('ironic/baremetal', 'interface')
6
+$baremetal_ipaddr  = get_network_role_property('ironic/baremetal', 'ipaddr')
7
+$baremetal_network = get_network_role_property('ironic/baremetal', 'network')
8
+
9
+# Firewall
10
+###############################
11
+firewallchain { 'baremetal:filter:IPv4':
12
+  ensure => present,
13
+} ->
14
+firewall { '101 allow TFTP':
15
+  chain  => 'baremetal',
16
+  source => $baremetal_network,
17
+  destination => $baremetal_ipaddr,
18
+  proto  => 'udp',
19
+  dport => '69',
20
+  action => 'accept',
21
+} ->
22
+firewall { '102 allow related':
23
+  chain  => 'baremetal',
24
+  source => $baremetal_network,
25
+  destination => $baremetal_ipaddr,
26
+  proto  => 'all',
27
+  state => ['RELATED', 'ESTABLISHED'],
28
+  action => 'accept',
29
+} ->
30
+firewall { '999 drop all':
31
+  chain  => 'baremetal',
32
+  action => 'drop',
33
+  proto  => 'all',
34
+} ->
35
+firewall {'00 baremetal-filter ':
36
+  proto   => 'all',
37
+  iniface => $baremetal_int,
38
+  jump => 'baremetal',
39
+  require => Class['openstack::firewall'],
40
+}
41
+
42
+exec { 'fix_ipt_modules':
43
+  command => '/bin/sed -i "s/^IPT_MODULES=.*/IPT_MODULES=\"nf_conntrack_ftp nf_nat_ftp nf_conntrack_netbios_ns nf_conntrack_tftp\"/g" /etc/default/ufw',
44
+  unless => '/bin/grep "^IPT_MODULES=.*nf_conntrack_tftp" /etc/default/ufw > /dev/null',
45
+  notify => Exec['load_tftp_mod']
46
+}
47
+
48
+exec { 'load_tftp_mod':
49
+  command => '/sbin/modprobe nf_conntrack_tftp',
50
+  refreshonly => true,
51
+}
52
+
53
+class { 'openstack::firewall':}
54
+

+ 21
- 0
deployment_scripts/puppet/manifests/network-ovs.pp View File

@@ -0,0 +1,21 @@
1
+notice('MODULAR: ironic/network-ovs.pp')
2
+
3
+$network_scheme    = hiera('network_scheme', {})
4
+prepare_network_config($network_scheme)
5
+$baremetal_int     = get_network_role_property('ironic/baremetal', 'interface')
6
+$sdn               = generate_network_config()
7
+
8
+# OVS patch
9
+###############################
10
+class { 'l23network':
11
+ use_ovs => true,
12
+} ->
13
+l23network::l2::bridge { 'br-ironic':
14
+  provider => 'ovs'
15
+} ->
16
+l23network::l2::patch { "patch__${baremetal_int}--br-ironic":
17
+  bridges => ['br-ironic', $baremetal_int],
18
+  provider => 'ovs',
19
+  mtu => 65000,
20
+}
21
+

+ 140
- 0
deployment_scripts/puppet/manifests/network.pp View File

@@ -0,0 +1,140 @@
1
+notice('MODULAR: ironic/network.pp')
2
+
3
+$network_scheme    = hiera('network_scheme', {})
4
+prepare_network_config($network_scheme)
5
+$network_metadata  = hiera_hash('network_metadata', {})
6
+$neutron_config    = hiera_hash('quantum_settings')
7
+$pnets             = $neutron_config['L2']['phys_nets']
8
+$baremetal_vip     = $network_metadata['vips']['baremetal']['ipaddr']
9
+$baremetal_int     = get_network_role_property('ironic/baremetal', 'interface')
10
+$baremetal_ipaddr  = get_network_role_property('ironic/baremetal', 'ipaddr')
11
+$baremetal_netmask = get_network_role_property('ironic/baremetal', 'netmask')
12
+$baremetal_network = get_network_role_property('ironic/baremetal', 'network')
13
+$nameservers       = $neutron_config['predefined_networks']['net04']['L3']['nameservers']
14
+
15
+$ironic_hash       = hiera_hash('fuel-plugin-ironic', {})
16
+$baremetal_L3_allocation_pool = $ironic_hash['l3_allocation_pool']
17
+$baremetal_L3_gateway = $ironic_hash['l3_gateway']
18
+
19
+
20
+# Firewall
21
+###############################
22
+firewallchain { 'baremetal:filter:IPv4':
23
+  ensure => present,
24
+} ->
25
+firewall { '100 allow ping from VIP':
26
+  chain       => 'baremetal',
27
+  source      => $baremetal_vip,
28
+  destination => $baremetal_ipaddr,
29
+  proto       => 'icmp',
30
+  icmp        => 'echo-request',
31
+  action      => 'accept',
32
+} ->
33
+firewall { '999 drop all':
34
+  chain  => 'baremetal',
35
+  action => 'drop',
36
+  proto  => 'all',
37
+} ->
38
+firewall {'00 baremetal-filter ':
39
+  proto   => 'all',
40
+  iniface => $baremetal_int,
41
+  jump => 'baremetal',
42
+  require => Class['openstack::firewall'],
43
+}
44
+
45
+class { 'openstack::firewall':}
46
+
47
+
48
+# VIP
49
+###############################
50
+$ns_iptables_start_rules = "iptables -A INPUT -i baremetal-ns -s ${baremetal_network} -d ${baremetal_vip} -p tcp -m multiport --dports 6385,8080 -m state --state NEW -j ACCEPT; iptables -A INPUT -i baremetal-ns -s ${baremetal_network} -d ${baremetal_vip} -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -A INPUT -i baremetal-ns -j DROP"
51
+$ns_iptables_stop_rules = "iptables -D INPUT -i baremetal-ns -s ${baremetal_network} -d ${baremetal_vip} -p tcp -m multiport --dports 6385,8080 -m state --state NEW -j ACCEPT; iptables -D INPUT -i baremetal-ns -s ${baremetal_network} -d ${baremetal_vip} -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -D INPUT -i baremetal-ns -j DROP"
52
+$baremetal_vip_data = {
53
+  namespace      => 'haproxy',
54
+  nic            => $baremetal_int,
55
+  base_veth      => 'baremetal-base',
56
+  ns_veth        => 'baremetal-ns',
57
+  ip             => $baremetal_vip,
58
+  cidr_netmask   => netmask_to_cidr($baremetal_netmask),
59
+  gateway        => 'none',
60
+  gateway_metric => '0',
61
+  bridge         => $baremetal_int,
62
+  ns_iptables_start_rules => $ns_iptables_start_rules,
63
+  ns_iptables_stop_rules  => $ns_iptables_stop_rules,
64
+  iptables_comment        => 'baremetal-filter',
65
+}
66
+
67
+cluster::virtual_ip { 'baremetal' :
68
+  vip => $baremetal_vip_data,
69
+}
70
+
71
+
72
+# Physnets
73
+###############################
74
+if $pnets['physnet1'] {
75
+  $physnet1 = "physnet1:${pnets['physnet1']['bridge']}"
76
+}
77
+if $pnets['physnet2'] {
78
+  $physnet2 = "physnet2:${pnets['physnet2']['bridge']}"
79
+}
80
+$physnet_ironic = "physnet-ironic:br-ironic"
81
+$physnets_array = [$physnet1, $physnet2, $physnet_ironic]
82
+$bridge_mappings = delete_undef_values($physnets_array)
83
+
84
+$br_map_str = join($bridge_mappings, ',')
85
+neutron_agent_ovs {
86
+  'ovs/bridge_mappings': value => $br_map_str;
87
+}
88
+
89
+$flat_networks  = ['physnet-ironic']
90
+neutron_plugin_ml2 {
91
+  'ml2_type_flat/flat_networks': value => join($flat_networks, ',');
92
+}
93
+
94
+service { 'p_neutron-plugin-openvswitch-agent':
95
+  ensure => 'running',
96
+  enable => true,
97
+  provider => 'pacemaker',
98
+}
99
+service { 'p_neutron-dhcp-agent':
100
+  ensure => 'running',
101
+  enable => true,
102
+  provider => 'pacemaker',
103
+}
104
+
105
+Neutron_plugin_ml2<||> ~> Service['p_neutron-plugin-openvswitch-agent'] ~> Service['p_neutron-dhcp-agent']
106
+Neutron_agent_ovs<||> ~> Service['p_neutron-plugin-openvswitch-agent'] ~> Service['p_neutron-dhcp-agent']
107
+
108
+
109
+# Predefined network
110
+###############################
111
+$netdata = {
112
+  'L2' => {
113
+    network_type => 'flat',
114
+    physnet => 'physnet-ironic',
115
+    router_ext => 'false',
116
+    segment_id => 'null'
117
+  },
118
+  'L3' => {
119
+    enable_dhcp => true,
120
+    floating => $baremetal_L3_allocation_pool,
121
+    gateway => $baremetal_L3_gateway,
122
+    nameservers => $nameservers,
123
+    subnet => $baremetal_network
124
+  },
125
+  'shared' => 'true',
126
+  'tenant' => 'admin',
127
+}
128
+
129
+openstack::network::create_network{'baremetal':
130
+  netdata           => $netdata,
131
+  segmentation_type => 'flat',
132
+} ->
133
+neutron_router_interface { "router04:baremetal__subnet":
134
+  ensure => present,
135
+}
136
+
137
+
138
+# Order
139
+###############################
140
+Firewall<||> -> Cluster::Virtual_ip<||> -> Neutron_plugin_ml2<||> -> Neutron_agent_ovs<||> -> Openstack::Network::Create_network<||>

+ 1
- 0
deployment_scripts/puppet/modules/ironic

@@ -0,0 +1 @@
1
+Subproject commit 69fa70013893a323a7cf62bc57963bd7a86bab04

+ 1
- 0
deployment_scripts/puppet/modules/tftp

@@ -0,0 +1 @@
1
+Subproject commit e7e5b5f1a38833e769453a848a7b20741039b415

+ 141
- 0
deployment_scripts/upload_images.rb View File

@@ -0,0 +1,141 @@
1
+#!/usr/bin/env ruby
2
+require 'hiera'
3
+
4
+ENV['LANG'] = 'C'
5
+
6
+hiera = Hiera.new(:config => '/etc/hiera.yaml')
7
+glanced = hiera.lookup 'glance', {} , {}
8
+management_vip = hiera.lookup 'management_vip', nil, {}
9
+auth_addr = hiera.lookup 'service_endpoint', "#{management_vip}", {}
10
+tenant_name = glanced['tenant'].nil? ? "services" : glanced['tenant']
11
+user_name = glanced['user'].nil? ? "glance" : glanced['user']
12
+endpoint_type = glanced['endpoint_type'].nil? ? "internalURL" : glanced['endpoint_type']
13
+region_name = hiera.lookup 'region', 'RegionOne', {}
14
+master_ip = hiera.lookup 'master_ip', nil, {}
15
+
16
+ENV['OS_TENANT_NAME']="#{tenant_name}"
17
+ENV['OS_USERNAME']="#{user_name}"
18
+ENV['OS_PASSWORD']="#{glanced['user_password']}"
19
+ENV['OS_AUTH_URL']="http://#{auth_addr}:5000/v2.0"
20
+ENV['OS_ENDPOINT_TYPE'] = "#{endpoint_type}"
21
+ENV['OS_REGION_NAME']="#{region_name}"
22
+
23
+ironic_images = [
24
+{"os_name"=>"ironic-deploy-linux",
25
+ "img_location"=>"http://#{master_ip}:8080/bootstrap/ironic/linux",
26
+ "container_format"=>"aki",
27
+ "min_ram"=>2048,
28
+ "disk_format"=>"aki",
29
+ "glance_properties"=>"",
30
+ "img_name"=>"ironic-deploy-linux",
31
+ "public"=>"true",
32
+ "protected"=>"true",
33
+},
34
+{"os_name"=>"ironic-deploy-initramfs",
35
+ "img_location"=>"http://#{master_ip}:8080/bootstrap/ironic/initramfs.img",
36
+ "container_format"=>"ari",
37
+ "min_ram"=>2048,
38
+ "disk_format"=>"ari",
39
+ "glance_properties"=>"",
40
+ "img_name"=>"ironic-deploy-initramfs",
41
+ "public"=>"true",
42
+ "protected"=>"true",
43
+},
44
+{"os_name"=>"ironic-deploy-squashfs",
45
+ "img_location"=>"http://#{master_ip}:8080/bootstrap/ironic/root.squashfs",
46
+ "container_format"=>"ari",
47
+ "min_ram"=>2048,
48
+ "disk_format"=>"ari",
49
+ "glance_properties"=>"",
50
+ "img_name"=>"ironic-deploy-squashfs",
51
+ "public"=>"true",
52
+ "protected"=>"true",
53
+},
54
+]
55
+
56
+ironic_images.each do |image|
57
+  %w(
58
+  disk_format
59
+  img_location
60
+  img_name
61
+  os_name
62
+  public
63
+  protected
64
+  container_format
65
+  min_ram
66
+  ).each do |f|
67
+    raise "Data field '#{f}' is missing!" unless image[f]
68
+  end
69
+end
70
+
71
+def image_list
72
+  stdout = `glance image-list`
73
+  return_code = $?.exitstatus
74
+  images = []
75
+  stdout.split("\n").each do |line|
76
+    fields = line.split('|').map { |f| f.chomp.strip }
77
+    next if fields[1] == 'ID'
78
+    next unless fields[2]
79
+    images << fields[2]
80
+  end
81
+  {:images => images, :exit_code => return_code}
82
+end
83
+
84
+def image_create(image_hash)
85
+  command = <<-EOF
86
+/usr/bin/glance image-create \
87
+--name '#{image_hash['img_name']}' \
88
+--is-public '#{image_hash['public']}' \
89
+--is-protected '#{image_hash['protected']}' \
90
+--container-format='#{image_hash['container_format']}' \
91
+--disk-format='#{image_hash['disk_format']}' \
92
+--min-ram='#{image_hash['min_ram']}' \
93
+#{image_hash['glance_properties']} \
94
+--copy-from '#{image_hash['img_location']}'
95
+EOF
96
+  puts command
97
+  stdout = `#{command}`
98
+  return_code = $?.exitstatus
99
+  [ stdout, return_code ]
100
+end
101
+
102
+# check if Glance is online
103
+# waited until the glance is started because when vCenter used as a glance
104
+# backend launch may takes up to 1 minute.
105
+def wait_for_glance
106
+  5.times.each do |retries|
107
+    sleep 10 if retries > 0
108
+    return if image_list[:exit_code] == 0
109
+  end
110
+  raise 'Could not get a list of glance images!'
111
+end
112
+
113
+# upload image to Glance
114
+# if it have not been already uploaded
115
+def upload_image(image)
116
+  list_of_images = image_list
117
+  if list_of_images[:images].include?(image['img_name']) && list_of_images[:exit_code] == 0
118
+    puts "Image '#{image['img_name']}' is already present!"
119
+    return 0
120
+  end
121
+
122
+  stdout, return_code = image_create(image)
123
+  if return_code == 0
124
+    puts "Image '#{image['img_name']}' was uploaded from '#{image['img_location']}'"
125
+  else
126
+    puts "Image '#{image['img_name']}' upload from '#{image['img_location']}' have FAILED!"
127
+  end
128
+  puts stdout
129
+  return return_code
130
+end
131
+
132
+########################
133
+
134
+wait_for_glance
135
+errors = 0
136
+
137
+ironic_images.each do |image|
138
+  errors += upload_image(image)
139
+end
140
+
141
+exit 1 unless errors == 0

+ 131
- 0
deployment_tasks.yaml View File

@@ -0,0 +1,131 @@
1
+- id: ironic-copy-bootstrap-keys
2
+  type: copy_files
3
+  role: ['ironic']
4
+  required_for: [pre_deployment_end]
5
+  requires: [pre_deployment_start]
6
+  parameters:
7
+    permissions: '0600'
8
+    dir_permissions: '0700'
9
+    files:
10
+      - src: /var/lib/fuel/keys/ironic/bootstrap.rsa
11
+        dst: /var/lib/astute/ironic/bootstrap.rsa
12
+
13
+- id: ironic-haproxy
14
+  groups: ['primary-controller', 'controller']
15
+  type: puppet
16
+  required_for: [ironic-api]
17
+  requires: [openstack-haproxy, ironic-network]
18
+  parameters:
19
+    puppet_manifest: puppet/manifests/haproxy.pp
20
+    puppet_modules: puppet/modules:/etc/puppet/modules
21
+    timeout: 3600
22
+
23
+- id: ironic-network-ovs
24
+  groups: ['primary-controller', 'controller']
25
+  type: puppet
26
+  required_for: [virtual_ips]
27
+  requires: [netconfig]
28
+  parameters:
29
+    puppet_manifest: puppet/manifests/network-ovs.pp
30
+    puppet_modules: puppet/modules:/etc/puppet/modules
31
+    timeout: 3600
32
+
33
+- id: ironic-network
34
+  groups: ['primary-controller', 'controller']
35
+  type: puppet
36
+  required_for: [ironic-haproxy]
37
+  requires: [openstack-controller, ironic-network-ovs]
38
+  parameters:
39
+    puppet_manifest: puppet/manifests/network.pp
40
+    puppet_modules: puppet/modules:/etc/puppet/modules
41
+    timeout: 3600
42
+
43
+- id: ironic-db
44
+  groups: ['primary-controller']
45
+  type: puppet
46
+  required_for: [ironic-api]
47
+  requires: [database]
48
+  parameters:
49
+    puppet_manifest: puppet/manifests/db.pp
50
+    puppet_modules: puppet/modules:/etc/puppet/modules
51
+    timeout: 3600
52
+
53
+- id: ironic-upload-images
54
+  role: ['primary-controller']
55
+  type: shell
56
+  required_for: [post_deployment_end]
57
+  requires: [enable_quorum]
58
+  parameters:
59
+    cmd: ruby upload_images.rb
60
+    retries: 3
61
+    interval: 20
62
+    timeout: 180
63
+
64
+- id: ironic-swift-key
65
+  role: ['primary-controller']
66
+  type: shell
67
+  required_for: [post_deployment_end]
68
+  requires: [enable_quorum]
69
+  parameters:
70
+    cmd: ruby post_swift_key.rb
71
+    retries: 3
72
+    interval: 20
73
+    timeout: 180
74
+
75
+- id: ironic-api
76
+  groups: ['primary-controller', 'controller']
77
+  type: puppet
78
+  required_for: [deploy_end, controller_remaining_tasks]
79
+  requires: [openstack-controller, ironic-db, ironic-network, ironic-haproxy, swift]
80
+  parameters:
81
+    puppet_manifest: puppet/manifests/ironic.pp
82
+    puppet_modules: puppet/modules:/etc/puppet/modules
83
+    timeout: 3600
84
+
85
+- id: ironic-network-conductor
86
+  groups: ['ironic']
87
+  type: puppet
88
+  required_for: [deploy_end, ironic-conductor]
89
+  requires: [hosts, firewall]
90
+  parameters:
91
+    puppet_manifest: puppet/manifests/network-conductor.pp
92
+    puppet_modules: puppet/modules:/etc/puppet/modules
93
+    timeout: 3600
94
+
95
+- id: ironic-conductor
96
+  groups: ['ironic']
97
+  type: puppet
98
+  required_for: [deploy_end, ironic-compute]
99
+  requires: [hosts, firewall, ironic-network-conductor]
100
+  parameters:
101
+    puppet_manifest: puppet/manifests/ironic-conductor.pp
102
+    puppet_modules: puppet/modules:/etc/puppet/modules
103
+    timeout: 3600
104
+
105
+- id: ironic-compute
106
+  groups: ['ironic']
107
+  type: puppet
108
+  required_for: [deploy_end]
109
+  requires: [hosts, firewall, ironic-conductor]
110
+  parameters:
111
+    puppet_manifest: puppet/manifests/ironic-compute.pp
112
+    puppet_modules: puppet/modules:/etc/puppet/modules
113
+    timeout: 3600
114
+
115
+- id: ironic
116
+  type: group
117
+  role: [ironic]
118
+  tasks:
119
+    - fuel_pkgs
120
+    - hiera
121
+    - globals
122
+    - logging
123
+    - tools
124
+    - netconfig
125
+    - hosts
126
+    - firewall
127
+  required_for: [deploy_end]
128
+  requires: [deploy_start]
129
+  parameters:
130
+    strategy:
131
+      type: parallel

+ 31
- 0
environment_config.yaml View File

@@ -0,0 +1,31 @@
1
+attributes:
2
+  metadata:
3
+    restrictions:
4
+      - "cluster:net_provider != 'neutron' or networking_parameters:segmentation_type != 'vlan'": "Ironic requires Neutron with VLAN segmentation."
5
+      - "settings:storage.images_ceph.value == true": "Ironic requires Swift as a backend for Glance image service."
6
+  password:
7
+    value: "I_love_plugins"
8
+    label: "Password for user, db and swift"
9
+    type: "text"
10
+    weight: 10
11
+    regex:
12
+      source: '^([a-zA-Z