puppet-classroom-manager 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +52 -0
- data/bin/classroom +115 -0
- data/lib/classroom/help.rb +59 -0
- data/lib/classroom/page.rb +41 -0
- data/lib/classroom/performance.rb +41 -0
- data/lib/classroom/reset.rb +121 -0
- data/lib/classroom/restart.rb +91 -0
- data/lib/classroom/sanitize.rb +76 -0
- data/lib/classroom/submit.rb +72 -0
- data/lib/classroom/troubleshoot.rb +178 -0
- data/lib/classroom/validate.rb +17 -0
- data/lib/classroom/version.rb +3 -0
- data/lib/classroom.rb +54 -0
- metadata +134 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 91bd91d0dc4b47cffaf1dcc62cb23f167e9ac4e5
|
|
4
|
+
data.tar.gz: 1f74d7f92c3b89f52528cbdf1771749d512508f9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c2a71798353538217e3e563360204406b0004ddb540969c80a2f56e0701afcec7506eeb8c18ce6cd50a819695289d4cd25ef161bad623f4949bf274c500c55c4
|
|
7
|
+
data.tar.gz: c2d572e1ac680bf66210f9e9b210bee4c372e66fff808c54f91c6058d73b45367e314b8f3b3c9cc40af4984f3404162a696ef2a122744dfda3062c19fa2f47bd
|
data/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Puppet Classroom Manager
|
|
2
|
+
|
|
3
|
+
Usage : `classroom <action> [subject] [options]`
|
|
4
|
+
|
|
5
|
+
Manage Puppet classroom VMs and updating courseware. This includes
|
|
6
|
+
troubleshooting and maintenance tasks to be used as needed during
|
|
7
|
+
the delivery and facilities for resetting the VM for use across
|
|
8
|
+
multiple deliveries. This is pre-installed on the classroom VM and
|
|
9
|
+
is only of use to trainers delivering Puppet Inc. classes.
|
|
10
|
+
|
|
11
|
+
Available actions:
|
|
12
|
+
|
|
13
|
+
* `classroom update`
|
|
14
|
+
This updates your VM to the latest released version. This includes all
|
|
15
|
+
cached packages, gems, modules, etc. and pre-installed courseware.
|
|
16
|
+
|
|
17
|
+
You should run this command before each delivery. It may take several
|
|
18
|
+
minutes to complete. When finished, it will run the validate task.
|
|
19
|
+
|
|
20
|
+
* `classroom validate`
|
|
21
|
+
This runs built in serverspec tests to check the VM for consistency.
|
|
22
|
+
It is run automatically by the update task so you likely don't need
|
|
23
|
+
to run it yourself.
|
|
24
|
+
|
|
25
|
+
* `classroom submit`
|
|
26
|
+
This task replaces the old `rake submit` tasks and uploads classroom
|
|
27
|
+
statistics to the EDU department for analysis.
|
|
28
|
+
|
|
29
|
+
* `classroom sanitize`
|
|
30
|
+
Running this task will remove all traces of students in preparation
|
|
31
|
+
for using the same VM for the next delivery.
|
|
32
|
+
|
|
33
|
+
* `classroom performance log <message>`
|
|
34
|
+
The VM periodically records performance metrics. These are included
|
|
35
|
+
in the information uploaded with the `submit` task. This task allows
|
|
36
|
+
you to annotate those logs with arbitrary information.
|
|
37
|
+
|
|
38
|
+
* `classroom reset password | certificates | filesync`
|
|
39
|
+
This task resets the specified information. Using this to reset the
|
|
40
|
+
root password ensures that it is still displayed on the login console.
|
|
41
|
+
This can also regenerate all monolithic PE certificates or use the
|
|
42
|
+
so-called "nuclear" option to blow away and redeploy filesync.
|
|
43
|
+
|
|
44
|
+
* `classroom restart <PE services>`
|
|
45
|
+
Gracefully restart PE services in the proper order. Run without a
|
|
46
|
+
service to see usage information.
|
|
47
|
+
|
|
48
|
+
* `classroom troubleshoot`
|
|
49
|
+
Run through various troubleshooting checks and fixes.
|
|
50
|
+
|
|
51
|
+
* `classroom page`
|
|
52
|
+
This will page on call classroom support in case of emergency.
|
data/bin/classroom
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#! /usr/bin/env ruby
|
|
2
|
+
require 'optparse'
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
5
|
+
require 'classroom'
|
|
6
|
+
|
|
7
|
+
$logger = Logger.new(STDOUT)
|
|
8
|
+
$logger.level = Logger::INFO
|
|
9
|
+
$logger.formatter = proc do |severity, datetime, progname, msg|
|
|
10
|
+
"#{severity}: #{msg}\n"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
config = {}
|
|
14
|
+
optparse = OptionParser.new { |opts|
|
|
15
|
+
opts.banner = "Usage : classroom <action> [subject] [options]
|
|
16
|
+
|
|
17
|
+
Manage Puppet classroom VMs and updating courseware. This includes
|
|
18
|
+
troubleshooting and maintenance tasks to be used as needed during
|
|
19
|
+
the delivery and facilities for resetting the VM for use across
|
|
20
|
+
multiple deliveries. This is pre-installed on the classroom VM.
|
|
21
|
+
|
|
22
|
+
Type courseware help for full usage instructions.
|
|
23
|
+
|
|
24
|
+
"
|
|
25
|
+
|
|
26
|
+
opts.on("-d", "--debug", "Display debugging messages") do
|
|
27
|
+
$logger.level = Logger::DEBUG
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
opts.on("-f", "--force", "When restarting services, force a hard restart.") do
|
|
31
|
+
config[:force] = true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
opts.separator('')
|
|
35
|
+
|
|
36
|
+
opts.on("-v", "--version", "Print out the version") do
|
|
37
|
+
require 'courseware/version'
|
|
38
|
+
puts Courseware::VERSION
|
|
39
|
+
exit
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
opts.on("-h", "--help", "Displays this help") do
|
|
43
|
+
puts opts
|
|
44
|
+
puts
|
|
45
|
+
exit 1
|
|
46
|
+
end
|
|
47
|
+
}
|
|
48
|
+
optparse.parse!
|
|
49
|
+
|
|
50
|
+
# hardcode for now. Decide if we need configurability later
|
|
51
|
+
config[:basedir] = '/opt/pltraining'
|
|
52
|
+
config[:bindir] = "#{config[:basedir]}/bin"
|
|
53
|
+
config[:confdir] = "#{config[:basedir]}/etc/puppet"
|
|
54
|
+
config[:specdir] = "#{config[:basedir]}/spec"
|
|
55
|
+
config[:learndot] = 'https://learn.puppet.com/manage/instructor/dashboard.html'
|
|
56
|
+
|
|
57
|
+
# grab the arguments after we've scraped the options out
|
|
58
|
+
verb, *subject = ARGV.collect {|arg| arg.to_sym }
|
|
59
|
+
|
|
60
|
+
classroom = Classroom.new(config)
|
|
61
|
+
begin
|
|
62
|
+
case verb
|
|
63
|
+
when :update
|
|
64
|
+
classroom.update
|
|
65
|
+
classroom.validate
|
|
66
|
+
|
|
67
|
+
when :validate
|
|
68
|
+
classroom.validate
|
|
69
|
+
# serverspec tests on the master MV
|
|
70
|
+
|
|
71
|
+
when :submit
|
|
72
|
+
classroom.submit
|
|
73
|
+
# submit classroom presentation stats
|
|
74
|
+
|
|
75
|
+
when :sanitize
|
|
76
|
+
classroom.sanitize
|
|
77
|
+
# remove users, etc to start a new delivery
|
|
78
|
+
|
|
79
|
+
when :performance
|
|
80
|
+
classroom.performance subject
|
|
81
|
+
# reset certificates, environments, node groups, courseware, etc
|
|
82
|
+
# is this even a thing???
|
|
83
|
+
|
|
84
|
+
when :reset
|
|
85
|
+
classroom.reset subject
|
|
86
|
+
# reset master certificates
|
|
87
|
+
# reset VM password
|
|
88
|
+
|
|
89
|
+
when :restart
|
|
90
|
+
classroom.restart subject
|
|
91
|
+
# restart PE services
|
|
92
|
+
|
|
93
|
+
when :troubleshoot
|
|
94
|
+
classroom.troubleshoot
|
|
95
|
+
# run troubleshooting scripts
|
|
96
|
+
|
|
97
|
+
when :page
|
|
98
|
+
classroom.page
|
|
99
|
+
|
|
100
|
+
when :help
|
|
101
|
+
classroom.help
|
|
102
|
+
|
|
103
|
+
when :shell
|
|
104
|
+
classroom.debug
|
|
105
|
+
|
|
106
|
+
else
|
|
107
|
+
$logger.warn "Unknown verb: #{verb}"
|
|
108
|
+
classroom.help
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
rescue RuntimeError => e
|
|
112
|
+
puts "Error: #{e.message}"
|
|
113
|
+
puts e.backtrace.join("\n\t") if config[:debug]
|
|
114
|
+
exit 1
|
|
115
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
HELP = <<-EOF
|
|
3
|
+
|
|
4
|
+
Usage : classroom <action> [subject] [options]
|
|
5
|
+
|
|
6
|
+
Manage Puppet classroom VMs and updating courseware. This includes
|
|
7
|
+
troubleshooting and maintenance tasks to be used as needed during
|
|
8
|
+
the delivery and facilities for resetting the VM for use across
|
|
9
|
+
multiple deliveries. This is pre-installed on the classroom VM and
|
|
10
|
+
is only of use to trainers delivering Puppet Inc. classes.
|
|
11
|
+
|
|
12
|
+
Available actions:
|
|
13
|
+
|
|
14
|
+
* classroom update:
|
|
15
|
+
This updates your VM to the latest released version. This includes all
|
|
16
|
+
cached packages, gems, modules, etc. and pre-installed courseware.
|
|
17
|
+
|
|
18
|
+
You should run this command before each delivery. It may take several
|
|
19
|
+
minutes to complete. When finished, it will run the validate task.
|
|
20
|
+
|
|
21
|
+
* classroom validate
|
|
22
|
+
This runs built in serverspec tests to check the VM for consistency.
|
|
23
|
+
It is run automatically by the update task so you likely don't need
|
|
24
|
+
to run it yourself.
|
|
25
|
+
|
|
26
|
+
* classroom submit
|
|
27
|
+
This task replaces the old `rake submit` tasks and uploads classroom
|
|
28
|
+
statistics to the EDU department for analysis.
|
|
29
|
+
|
|
30
|
+
* classroom sanitize
|
|
31
|
+
Running this task will remove all traces of students in preparation
|
|
32
|
+
for using the same VM for the next delivery.
|
|
33
|
+
|
|
34
|
+
* classroom performance log <message>
|
|
35
|
+
The VM periodically records performance metrics. These are included
|
|
36
|
+
in the information uploaded with the `submit` task. This task allows
|
|
37
|
+
you to annotate those logs with arbitrary information.
|
|
38
|
+
|
|
39
|
+
* classroom reset password | certificates | filesync
|
|
40
|
+
This task resets the specified information. Using this to reset the
|
|
41
|
+
root password ensures that it is still displayed on the login console.
|
|
42
|
+
This can also regenerate all monolithic PE certificates or use the
|
|
43
|
+
so-called "nuclear" option to blow away and redeploy filesync.
|
|
44
|
+
|
|
45
|
+
* classroom restart <PE services>
|
|
46
|
+
Gracefully restart PE services in the proper order. Run without a
|
|
47
|
+
service to see usage information.
|
|
48
|
+
|
|
49
|
+
* classroom troubleshoot
|
|
50
|
+
Run through various troubleshooting checks and fixes.
|
|
51
|
+
|
|
52
|
+
* classroom page
|
|
53
|
+
This will page on call classroom support in case of emergency.
|
|
54
|
+
|
|
55
|
+
* classroom help
|
|
56
|
+
You're looking at it!
|
|
57
|
+
|
|
58
|
+
EOF
|
|
59
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def page
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'rest-client'
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
config = JSON.parse(File.read('/opt/pltraining/etc/classroom.json'))
|
|
8
|
+
pd_key = File.read('/opt/pltraining/etc/pagerduty.key')
|
|
9
|
+
rescue => e
|
|
10
|
+
puts "Cannot load configuration"
|
|
11
|
+
puts e.message
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
puts "--------- You're about to page and possibly wake somone up. ---------"
|
|
16
|
+
puts "Please check the Troubleshooting Guide for solutions to common problems."
|
|
17
|
+
puts
|
|
18
|
+
puts "https://github.com/puppetlabs/courseware/blob/master/TroubleshootingGuide.md"
|
|
19
|
+
puts
|
|
20
|
+
if confirm?("Have you done everything in the troubleshooting guide?") then
|
|
21
|
+
puts "Sending page. Make sure you've posted about the issue in HipChat."
|
|
22
|
+
config = load_metadata
|
|
23
|
+
page_message = "Instructor: #{config['name']}\n" +
|
|
24
|
+
"Email: #{config['email']}\n" +
|
|
25
|
+
"Course: #{config['course']}\n" +
|
|
26
|
+
"ID: #{config['event_id']}"
|
|
27
|
+
page_data = {
|
|
28
|
+
"service_key" => pd_key,
|
|
29
|
+
"event_type" => "trigger",
|
|
30
|
+
"description" => page_message
|
|
31
|
+
}
|
|
32
|
+
response = RestClient.post(
|
|
33
|
+
"https://events.pagerduty.com/generic/2010-04-15/create_event.json",
|
|
34
|
+
page_data.to_json,
|
|
35
|
+
:content_type => :json,
|
|
36
|
+
:accept => :json
|
|
37
|
+
)
|
|
38
|
+
puts response
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def performance(subject)
|
|
3
|
+
if subject.empty?
|
|
4
|
+
puts "This will take performance related snapshots, which will be uploaded for"
|
|
5
|
+
puts "engineering analysis. You may also record performance notes into the log."
|
|
6
|
+
puts
|
|
7
|
+
puts "Usage: classroom performance [ log <message> | snapshot ]"
|
|
8
|
+
puts
|
|
9
|
+
puts " * log: Record a message into classroom log and take a snapshot."
|
|
10
|
+
puts " * snapshot: Save snapshot of classroom statistics."
|
|
11
|
+
puts
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
case subject.shift
|
|
16
|
+
when 'log'
|
|
17
|
+
message = subject.empty? ? "Misc performance issue noted." : subject.join(' ')
|
|
18
|
+
$logger.warn message
|
|
19
|
+
record_snapshot
|
|
20
|
+
|
|
21
|
+
when 'snapshot'
|
|
22
|
+
$logger.debug "Scheduled performance snapshot"
|
|
23
|
+
record_snapshot
|
|
24
|
+
|
|
25
|
+
else
|
|
26
|
+
raise "No such action"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def record_snapshot
|
|
30
|
+
$logger.debug "-------------------------------- top -bn1 ----------------------------------\n#{`top -bn1`}"
|
|
31
|
+
$logger.debug "-------------------------------- vmstat ------------------------------------\n#{`vmstat`}"
|
|
32
|
+
$logger.debug "-------------------------------- netstat -a --------------------------------\n#{`netstat -a`}"
|
|
33
|
+
$logger.debug "-------------------------------- iostat ------------------------------------\n#{`iostat`}"
|
|
34
|
+
$logger.debug "-------------------------------- mpstat -P ALL -----------------------------\n#{`mpstat -P ALL`}"
|
|
35
|
+
|
|
36
|
+
FileUtils.mkdir_p '/var/log/puppetlabs/classroom-traffic'
|
|
37
|
+
`tcpdump -G 15 -W 1 -w /var/log/puppetlabs/classroom-traffic/#{Time.now.to_i}.pcap -i any > /dev/null 2>&1 &`
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def reset(subject)
|
|
3
|
+
if subject.size != 1
|
|
4
|
+
puts <<-EOF
|
|
5
|
+
Usage: classroom reset <password | certificates | filesync>
|
|
6
|
+
|
|
7
|
+
This tool will reset or regenerate:
|
|
8
|
+
* root's login password and update the /etc/issue screen
|
|
9
|
+
* delete and redeploy all FileSync caches
|
|
10
|
+
* *all* ssl certificates in the PE stack. (warning: destructive!)
|
|
11
|
+
|
|
12
|
+
EOF
|
|
13
|
+
exit 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
case subject.first
|
|
17
|
+
when :password
|
|
18
|
+
reset_password
|
|
19
|
+
when :certificates
|
|
20
|
+
reset_certificates
|
|
21
|
+
when :filesync
|
|
22
|
+
reset_filesync
|
|
23
|
+
else
|
|
24
|
+
raise "Unknown action."
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def reset_password
|
|
29
|
+
print "Enter new root password: "
|
|
30
|
+
password = gets.chomp
|
|
31
|
+
|
|
32
|
+
%x(echo "root:#{password}"|chpasswd)
|
|
33
|
+
|
|
34
|
+
File.open('/var/local/password','w') do |f|
|
|
35
|
+
f.puts password
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
%x(/etc/rc.local 2>/dev/null)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def reset_certificates
|
|
42
|
+
require "fileutils"
|
|
43
|
+
|
|
44
|
+
# Automate the process of regenerating certificates on a monolithic master
|
|
45
|
+
# https://docs.puppet.com/pe/latest/trouble_regenerate_certs_monolithic.html
|
|
46
|
+
#
|
|
47
|
+
timestamp = Time.now.to_i
|
|
48
|
+
certname = `puppet master --configprint certname`.strip
|
|
49
|
+
ssldir = '/etc/puppetlabs/puppet/ssl'
|
|
50
|
+
puppetdbcerts = '/etc/puppetlabs/puppetdb/ssl'
|
|
51
|
+
consolecerts = '/opt/puppetlabs/server/data/console-services/certs'
|
|
52
|
+
pgsqlcerts = '/opt/puppetlabs/server/data/postgresql/9.4/data/certs'
|
|
53
|
+
orchcerts = '/etc/puppetlabs/orchestration-services/ssl'
|
|
54
|
+
|
|
55
|
+
["puppet", "puppetdb", "console-services", "postgresql", "orchestration"].each do |path|
|
|
56
|
+
FileUtils.mkdir_p("/root/certificates.bak/#{path}")
|
|
57
|
+
end
|
|
58
|
+
FileUtils.cp_r("#{ssldir}", "/root/certificates.bak/puppet/#{timestamp}")
|
|
59
|
+
FileUtils.cp_r("#{puppetdbcerts}", "/root/certificates.bak/puppetdb/#{timestamp}")
|
|
60
|
+
FileUtils.cp_r("#{consolecerts}", "/root/certificates.bak/console-services/#{timestamp}")
|
|
61
|
+
FileUtils.cp_r("#{pgsqlcerts}", "/root/certificates.bak/postgresql/#{timestamp}")
|
|
62
|
+
FileUtils.cp_r("#{orchcerts}", "/root/certificates.bak/orchestration/#{timestamp}")
|
|
63
|
+
|
|
64
|
+
puts "Certificates backed up to ~/certificates.bak"
|
|
65
|
+
|
|
66
|
+
puts
|
|
67
|
+
puts
|
|
68
|
+
puts "#####################################################################"
|
|
69
|
+
puts
|
|
70
|
+
puts " If you regenerate the Puppet CA to start fresh, then"
|
|
71
|
+
puts "ALL client certificates will be invalidated and must be regenerated!"
|
|
72
|
+
puts
|
|
73
|
+
puts " -- This should only be done as a last resort --"
|
|
74
|
+
puts
|
|
75
|
+
puts "#####################################################################"
|
|
76
|
+
puts
|
|
77
|
+
if confirm?('Would you like to regenerate the CA?', false)
|
|
78
|
+
FileUtils.rm_rf("#{ssldir}/*")
|
|
79
|
+
system("puppet cert list -a")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
FileUtils.rm_f("/opt/puppetlabs/puppet/cache/client_data/catalog/#{certname}.json")
|
|
83
|
+
system("puppet cert clean #{certname}")
|
|
84
|
+
system("puppet infrastructure configure --no-recover")
|
|
85
|
+
system("puppet agent -t")
|
|
86
|
+
|
|
87
|
+
puts "All done. If you regenerated the CA, then regenerate all client certificates now."
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def reset_filesync
|
|
91
|
+
puts
|
|
92
|
+
puts
|
|
93
|
+
puts "################################################################################"
|
|
94
|
+
puts
|
|
95
|
+
puts "This script will completely delete and redeploy all environments without backup!"
|
|
96
|
+
puts " The operation may take up to five minutes to complete."
|
|
97
|
+
puts
|
|
98
|
+
puts "################################################################################"
|
|
99
|
+
puts
|
|
100
|
+
bailout?
|
|
101
|
+
|
|
102
|
+
system("systemctl stop pe-puppetserver")
|
|
103
|
+
|
|
104
|
+
# filesync cache
|
|
105
|
+
FileUtils.rm_rf("/opt/puppetlabs/server/data/puppetserver/filesync")
|
|
106
|
+
|
|
107
|
+
# r10k cache
|
|
108
|
+
FileUtils.rm_rf("/opt/puppetlabs/server/data/code-manager/git")
|
|
109
|
+
|
|
110
|
+
# code manager worker thread caches
|
|
111
|
+
FileUtils.rm_rf("/opt/puppetlabs/server/data/code-manager/worker-caches")
|
|
112
|
+
FileUtils.rm_rf("/opt/puppetlabs/server/data/code-manager/cache")
|
|
113
|
+
|
|
114
|
+
# possibly stale environment codebases
|
|
115
|
+
FileUtils.rm_rf("/etc/puppetlabs/code/*")
|
|
116
|
+
FileUtils.rm_rf("/etc/puppetlabs/code-staging/environments")
|
|
117
|
+
|
|
118
|
+
system("systemctl start pe-puppetserver")
|
|
119
|
+
system("puppet code deploy --all --wait")
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def restart(subject)
|
|
3
|
+
if subject.empty?
|
|
4
|
+
puts <<-EOF
|
|
5
|
+
This tool simply helps restart the PE services in the right order.
|
|
6
|
+
It will send a HUP signal to Puppetserver by default which is much
|
|
7
|
+
faster than a full restart.
|
|
8
|
+
|
|
9
|
+
You can also restart Docker containers in classes that use them.
|
|
10
|
+
|
|
11
|
+
If you do need the full restart, please pass the -f option.
|
|
12
|
+
|
|
13
|
+
Service names:
|
|
14
|
+
* puppetserver
|
|
15
|
+
* console
|
|
16
|
+
* puppetdb
|
|
17
|
+
* orchestration
|
|
18
|
+
* mcollective
|
|
19
|
+
* containers
|
|
20
|
+
* all (restart all PE services in the proper order)
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
* classroom restart puppetdb puppetserver
|
|
24
|
+
* classroom restart puppetserver console -f
|
|
25
|
+
* classroom restart all -f
|
|
26
|
+
|
|
27
|
+
EOF
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# normalize to lowercase strings so we can pattern match
|
|
32
|
+
subject.map! { |x| x.to_s.downcase }
|
|
33
|
+
|
|
34
|
+
if subject.include? 'all'
|
|
35
|
+
puts "Restarting all PE stack services. This may take a few minutes..."
|
|
36
|
+
subject.concat ['puppetdb', 'puppetserver', 'orchestrator', 'console', 'mcollective', 'puppet']
|
|
37
|
+
subject.uniq!
|
|
38
|
+
else
|
|
39
|
+
puts "Restarting selected PE components."
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if subject.grep(/puppetdb|pdb/).any?
|
|
43
|
+
restart_service('pe-postgresql.service')
|
|
44
|
+
restart_service('pe-puppetdb.service')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if subject.grep(/puppetserver|server/).any?
|
|
48
|
+
if @config[:force]
|
|
49
|
+
restart_service('pe-puppetserver.service')
|
|
50
|
+
else
|
|
51
|
+
reload_service('pe-puppetserver.service', 'puppet-server')
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if subject.grep(/orch|pxp/).any?
|
|
56
|
+
restart_service('pe-orchestration-services.service')
|
|
57
|
+
restart_service('pxp-agent.service')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if subject.grep(/console/).any?
|
|
61
|
+
restart_service('pe-console-services.service')
|
|
62
|
+
restart_service('pe-nginx.service')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if subject.grep(/mco/).any?
|
|
66
|
+
restart_service('pe-activemq.service')
|
|
67
|
+
restart_service('mcollective.service')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if subject.include? 'puppet'
|
|
71
|
+
restart_service('puppet.service')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if subject.include? 'containers'
|
|
75
|
+
`systemctl list-units`.each_line do |line|
|
|
76
|
+
restart_service($1) if line =~ /^(docker-\S+)/
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def restart_service(service)
|
|
83
|
+
puts "- Restarting #{service}..."
|
|
84
|
+
system("systemctl restart #{service}")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def reload_service(service, pattern)
|
|
88
|
+
puts "> Reloading #{service}..."
|
|
89
|
+
system("kill -HUP `pgrep -f #{pattern}`")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def sanitize
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'puppetclassify'
|
|
6
|
+
|
|
7
|
+
puts 'Sanitizing your VM for your next delivery...'
|
|
8
|
+
|
|
9
|
+
certname = `puppet master --configprint certname`.strip
|
|
10
|
+
master = `puppet agent --configprint server`.strip
|
|
11
|
+
classifier = "http://#{master}:4433/classifier-api"
|
|
12
|
+
known_groups = [ 'All Nodes', 'Agent-specified environment', 'Production environment', /PE / ]
|
|
13
|
+
known_users = [ 'admin"=', 'api_user', 'deployer' ]
|
|
14
|
+
auth_info = {
|
|
15
|
+
'ca_certificate_path' => `puppet master --configprint localcacert`.strip,
|
|
16
|
+
'certificate_path' => `puppet master --configprint hostcert`.strip,
|
|
17
|
+
'private_key_path' => `puppet master --configprint hostprivkey`.strip,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
group_pattern = Regexp.union(known_groups)
|
|
21
|
+
puppetclassify = PuppetClassify.new(classifier, auth_info)
|
|
22
|
+
puppetclassify.groups.get_groups.each do |group|
|
|
23
|
+
next if group['name'].match(group_pattern)
|
|
24
|
+
puppetclassify.groups.delete_group(group['id'])
|
|
25
|
+
print '.'
|
|
26
|
+
end
|
|
27
|
+
puts
|
|
28
|
+
|
|
29
|
+
# depends on pltraining/rbac module
|
|
30
|
+
users = YAML.load(`puppet resource rbac_user --to_yaml`)
|
|
31
|
+
users['rbac_user'].each do |user, data|
|
|
32
|
+
next if known_users.include? user
|
|
33
|
+
puts "puppet resource rbac_user #{user} ensure=absent"
|
|
34
|
+
system("puppet resource rbac_user #{user} ensure=absent")
|
|
35
|
+
print '.'
|
|
36
|
+
end
|
|
37
|
+
puts
|
|
38
|
+
|
|
39
|
+
`puppet cert list --all --machine-readable`.each_line do |line|
|
|
40
|
+
next unless line.start_with? '+'
|
|
41
|
+
name = line.gsub('"', '').split[1]
|
|
42
|
+
next if name.start_with? 'pe-internal'
|
|
43
|
+
next if name == certname
|
|
44
|
+
|
|
45
|
+
system("puppet node deactivate #{name}")
|
|
46
|
+
system("puppet cert clean #{name}")
|
|
47
|
+
print '.'
|
|
48
|
+
end
|
|
49
|
+
puts
|
|
50
|
+
|
|
51
|
+
Dir.glob('/home/*').each do |path|
|
|
52
|
+
next if ['/home/training', '/home/showoff'].include? path
|
|
53
|
+
system("userdel #{File.basename(path)}")
|
|
54
|
+
FileUtils.rm_rf path
|
|
55
|
+
print '.'
|
|
56
|
+
end
|
|
57
|
+
puts
|
|
58
|
+
|
|
59
|
+
Dir.glob('/etc/puppetlabs/code-staging/environments/*').each do |path|
|
|
60
|
+
next if File.basename(path) == 'production'
|
|
61
|
+
FileUtils.rm_rf path
|
|
62
|
+
print '.'
|
|
63
|
+
end
|
|
64
|
+
puts
|
|
65
|
+
|
|
66
|
+
Dir.glob('/etc/puppetlabs/code/environments/*').each do |path|
|
|
67
|
+
next if File.basename(path) == 'production'
|
|
68
|
+
FileUtils.rm_rf path
|
|
69
|
+
print '.'
|
|
70
|
+
end
|
|
71
|
+
puts
|
|
72
|
+
|
|
73
|
+
FileUtils.rm_rf('/var/repositories/*')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def submit
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'aws-sdk'
|
|
5
|
+
|
|
6
|
+
if puppetlabs_instructor?
|
|
7
|
+
puts "Please go to your learn dashboard and ensure that attendance is accurate"
|
|
8
|
+
puts "and then close this class delivery to mark it as complete."
|
|
9
|
+
puts " -- #{@config[:learndot]}"
|
|
10
|
+
puts
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
presentation = showoff_working_directory()
|
|
14
|
+
|
|
15
|
+
data = JSON.parse(File.read("#{presentation}/stats/metadata.json")) rescue {}
|
|
16
|
+
event_id = data['event_id'] || Time.now.to_i
|
|
17
|
+
course = data['course'] || 'none'
|
|
18
|
+
email = data['email'] || 'none'
|
|
19
|
+
|
|
20
|
+
begin
|
|
21
|
+
# depends on root's credentials as managed by bootstrap
|
|
22
|
+
s3 = Aws::S3::Resource.new(region:'us-west-2')
|
|
23
|
+
|
|
24
|
+
# record the module versions in use
|
|
25
|
+
system("puppet module list > /var/log/puppetlabs/classroom-modules")
|
|
26
|
+
|
|
27
|
+
filename = "classroom-perflogs-#{course}-#{email}-#{event_id}.tar.gz"
|
|
28
|
+
system("tar -cf /var/cache/#{filename} /var/log/puppetlabs/")
|
|
29
|
+
obj = s3.bucket(PERF_BUCKET).object(filename)
|
|
30
|
+
obj.upload_file("/var/cache/#{filename}")
|
|
31
|
+
FileUtils.rm(filename)
|
|
32
|
+
|
|
33
|
+
filename = "classroom-stats-#{course}-#{email}-#{event_id}.tar.gz"
|
|
34
|
+
system("tar -cf /var/cache/#{filename} #{presentation}/stats/")
|
|
35
|
+
obj = s3.bucket(STATS_BUCKET).object(filename)
|
|
36
|
+
obj.upload_file("/var/cache/#{filename}")
|
|
37
|
+
FileUtils.rm(filename)
|
|
38
|
+
|
|
39
|
+
rescue LoadError, StandardError => e
|
|
40
|
+
LOGGER.warn "S3 upload failed. No network?"
|
|
41
|
+
LOGGER.warn e.message
|
|
42
|
+
LOGGER.debug e.backtrace
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# clean up for next delivery
|
|
46
|
+
system("puppet resource service showoff-courseware ensure=stopped")
|
|
47
|
+
FileUtils.rm_rf("#{presentation}/stats")
|
|
48
|
+
FileUtils.rm_f("#{presentation}/courseware.yaml")
|
|
49
|
+
FileUtils.rm_f("#{presentation}/_files/share/nearby_events.html")
|
|
50
|
+
system("puppet resource service showoff-courseware ensure=running")
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def puppetlabs_instructor?
|
|
55
|
+
# TODO: how do?
|
|
56
|
+
false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def showoff_working_directory
|
|
60
|
+
# get the path of the currently configured showoff presentation
|
|
61
|
+
data = {}
|
|
62
|
+
path = '/usr/lib/systemd/system/showoff-courseware.service'
|
|
63
|
+
File.read(path).each_line do |line|
|
|
64
|
+
setting = line.split('=')
|
|
65
|
+
next unless setting.size == 2
|
|
66
|
+
|
|
67
|
+
data[setting.first] = setting.last
|
|
68
|
+
end
|
|
69
|
+
data['WorkingDirectory']
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def troubleshoot
|
|
3
|
+
master = `puppet agent --configprint server`.strip
|
|
4
|
+
codedir = `puppet master --configprint codedir`.strip
|
|
5
|
+
filesync = '/etc/puppetlabs/puppetserver/conf.d/file-sync.conf'
|
|
6
|
+
release = File.read('/etc/puppetlabs-release').to_f rescue 0
|
|
7
|
+
legacy = release < 7.0
|
|
8
|
+
|
|
9
|
+
if File.exist? filesync
|
|
10
|
+
# why isn't there a configprint setting for this?
|
|
11
|
+
staging = `hocon -f #{filesync} get file-sync.repos.puppet-code.staging-dir`.strip
|
|
12
|
+
puts "Running checks for Code Manager configurations:"
|
|
13
|
+
else
|
|
14
|
+
staging = codedir
|
|
15
|
+
puts "Running checks for configurations without Code Manager:"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
print "Cleaning any stray .git directories in: #{codedir}..."
|
|
19
|
+
sleep 1
|
|
20
|
+
system("find #{codedir} -name .git -type d -print -exec rm -rf {} \\;")
|
|
21
|
+
check_success
|
|
22
|
+
|
|
23
|
+
print "Validating permissions on: #{codedir}..."
|
|
24
|
+
sleep 1
|
|
25
|
+
system("find #{codedir} '!' -user pe-puppet -print -exec chown pe-puppet:pe-puppet {} \\;")
|
|
26
|
+
check_success
|
|
27
|
+
|
|
28
|
+
if codedir != staging
|
|
29
|
+
puts "Validating permissions on: #{staging}..."
|
|
30
|
+
sleep 1
|
|
31
|
+
system("find #{staging} '!' -user pe-puppet -print -exec chown pe-puppet:pe-puppet {} \\;")
|
|
32
|
+
check_success
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# only check legacy systems that rely on manual installs
|
|
36
|
+
if legacy
|
|
37
|
+
if File.exist? '/home/training/courseware'
|
|
38
|
+
print "Sanitizing uploaded courseware..."
|
|
39
|
+
sleep 1
|
|
40
|
+
FileUtils.rm_f '/home/training/courseware/stats/viewstats.json'
|
|
41
|
+
FileUtils.rm_f '/home/training/courseware/stats/forms.json'
|
|
42
|
+
check_success
|
|
43
|
+
else
|
|
44
|
+
check_success(false)
|
|
45
|
+
puts "\tYou don't seem to have uploaded the courseware from your host system"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
print "Checking Forge connection..."
|
|
50
|
+
if system("ping -c1 -W2 forge.puppet.com >/dev/null 2>&1")
|
|
51
|
+
if legacy
|
|
52
|
+
puts "Ensuring the latest version of pltraining/classroom in #{staging}..."
|
|
53
|
+
system("puppet module upgrade pltraining/classroom --modulepath #{staging}")
|
|
54
|
+
check_success
|
|
55
|
+
else
|
|
56
|
+
check_success(true)
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
if `awk '$1 == "server" {print $2}' /etc/ntp.conf` != master
|
|
60
|
+
check_success(false)
|
|
61
|
+
puts "\tCould not reach the Forge. You should classify your master as $offline => true"
|
|
62
|
+
else
|
|
63
|
+
puts "\tYou appear to be in offline mode."
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if codedir != staging
|
|
68
|
+
print "Ensuring you have a valid deploy token..."
|
|
69
|
+
if File.exist? '/root/.puppetlabs/token'
|
|
70
|
+
token = `puppet access show`
|
|
71
|
+
api = 'https://#{master}:4433/rbac-api/v1/users/current'
|
|
72
|
+
status = `curl -k --write-out "%{http_code}" --silent --output /dev/null #{api} -H "X-Authentication:#{token}"`.strip
|
|
73
|
+
if status != "200"
|
|
74
|
+
print "\nRegenerating invalid token..."
|
|
75
|
+
FileUtils.rm_f('/root/.puppetlabs/token')
|
|
76
|
+
check_success
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
unless File.exist? '/root/.puppetlabs/token'
|
|
81
|
+
print "\nGenerating new token."
|
|
82
|
+
system('puppet plugin download > /dev/null')
|
|
83
|
+
system('puppet resource rbac_user deployer ensure=present display_name=deployer email=deployer@puppetlabs.vm password=puppetlabs roles=4 > /dev/null')
|
|
84
|
+
system('echo "puppetlabs" | HOME=/root /opt/puppetlabs/bin/puppet-access login deployer --lifetime 14d > /dev/null')
|
|
85
|
+
check_success
|
|
86
|
+
else
|
|
87
|
+
check_success(true)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
puts
|
|
91
|
+
puts "If you're having trouble with Code Manager or FileSync, deleting all deployed"
|
|
92
|
+
puts "code and destroying all caches can sometimes help you get going again."
|
|
93
|
+
puts
|
|
94
|
+
if confirm?('Would you like to nuke it all and start over?', false)
|
|
95
|
+
reset([:filesync])
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
print "Validating SSL certificates..."
|
|
100
|
+
if valid_certificates
|
|
101
|
+
check_success(true)
|
|
102
|
+
else
|
|
103
|
+
puts
|
|
104
|
+
puts "It looks like there is an inconsistency with your master's SSL certificates."
|
|
105
|
+
puts "Regenerating certificates may take up to five minutes."
|
|
106
|
+
puts
|
|
107
|
+
if confirm?('Would you like to try regenerating certificates?', false)
|
|
108
|
+
reset([:certificates])
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
puts
|
|
113
|
+
puts 'Done checking. Fix any errors noted above and try again.'
|
|
114
|
+
puts 'If still having troubles, try some of the following steps.'
|
|
115
|
+
puts 'Note that both tail and journalctl have a "-f" follow mode.'
|
|
116
|
+
puts
|
|
117
|
+
puts 'Log files:'
|
|
118
|
+
puts ' * tail /var/log/puppetlabs/puppetserver/puppetserver.log'
|
|
119
|
+
puts ' * tail /var/log/puppetlabs/console-services/console-services.log'
|
|
120
|
+
puts ' * tail any other interesting log files in /var/log/puppetlabs'
|
|
121
|
+
puts 'System logs:'
|
|
122
|
+
puts ' * journalctl -eu pe-puppetserver'
|
|
123
|
+
puts ' * journalctl -eu pe-console-services'
|
|
124
|
+
puts ' * systemctl list-units | egrep "pe-|puppet"'
|
|
125
|
+
puts 'Edu tools:'
|
|
126
|
+
puts ' * tail /var/log/puppetfactory'
|
|
127
|
+
puts ' * journalctl -eu abalone'
|
|
128
|
+
puts ' * journalctl -eu puppetfactory'
|
|
129
|
+
puts ' * journalctl -eu showoff-courseware'
|
|
130
|
+
puts ' * reset_ssl_certificates.sh'
|
|
131
|
+
puts ' * restart_classroom_services.rb'
|
|
132
|
+
puts ' * dependency_nuke.rb'
|
|
133
|
+
puts
|
|
134
|
+
puts 'Have you searched the Troubleshooting Guide for your issue?'
|
|
135
|
+
puts "If you're still stuck, page the on-call support with 'classroom page'"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def valid_certificates
|
|
139
|
+
certname = `puppet master --configprint certname`.strip
|
|
140
|
+
ssldir = '/etc/puppetlabs/puppet/ssl'
|
|
141
|
+
puppetdbcerts = '/etc/puppetlabs/puppetdb/ssl'
|
|
142
|
+
consolecerts = '/opt/puppetlabs/server/data/console-services/certs'
|
|
143
|
+
pgsqlcerts = '/opt/puppetlabs/server/data/postgresql/9.4/data/certs'
|
|
144
|
+
orchcerts = '/etc/puppetlabs/orchestration-services/ssl'
|
|
145
|
+
|
|
146
|
+
cert = same_file("#{ssldir}/certs/#{certname}.pem", [
|
|
147
|
+
"#{puppetdbcerts}/#{certname}.cert.pem",
|
|
148
|
+
"#{pgsqlcerts}/_local.cert.pem",
|
|
149
|
+
"#{consolecerts}/#{certname}.cert.pem",
|
|
150
|
+
"#{orchcerts}/#{certname}.cert.pem",
|
|
151
|
+
])
|
|
152
|
+
public_key = same_file("#{ssldir}/public_keys/#{certname}.pem", [
|
|
153
|
+
"#{puppetdbcerts}/#{certname}.public_key.pem",
|
|
154
|
+
"#{consolecerts}/#{certname}.public_key.pem",
|
|
155
|
+
"#{orchcerts}/#{certname}.public_key.pem",
|
|
156
|
+
])
|
|
157
|
+
private_key = same_file("#{ssldir}/private_keys/#{certname}.pem", [
|
|
158
|
+
"#{puppetdbcerts}/#{certname}.private_key.pem",
|
|
159
|
+
"#{pgsqlcerts}/_local.private_key.pem",
|
|
160
|
+
"#{consolecerts}/#{certname}.private_key.pem",
|
|
161
|
+
"#{orchcerts}/#{certname}.private_key.pem",
|
|
162
|
+
])
|
|
163
|
+
|
|
164
|
+
return (cert and public_key and private_key)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def same_file(filename, list)
|
|
168
|
+
require 'digest'
|
|
169
|
+
list = Array(list) # coerce if needed
|
|
170
|
+
left = Digest::MD5.hexdigest(File.read(filename))
|
|
171
|
+
|
|
172
|
+
list.each do |path|
|
|
173
|
+
return false unless (left == Digest::MD5.hexdigest(File.read(path)))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
return true
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
def validate
|
|
3
|
+
require 'rake'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
|
|
6
|
+
puts "Validating configuration..."
|
|
7
|
+
Dir.chdir(@config[:specdir]) do
|
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
9
|
+
t.rspec_opts = "-I #{@config[:specdir]}"
|
|
10
|
+
t.pattern = 'localhost/*_spec.rb'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Rake::Task[:spec].invoke
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/classroom.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class Classroom
|
|
2
|
+
require 'classroom/performance'
|
|
3
|
+
require 'classroom/reset'
|
|
4
|
+
require 'classroom/restart'
|
|
5
|
+
require 'classroom/sanitize'
|
|
6
|
+
require 'classroom/submit'
|
|
7
|
+
require 'classroom/troubleshoot'
|
|
8
|
+
require 'classroom/validate'
|
|
9
|
+
require 'classroom/version'
|
|
10
|
+
|
|
11
|
+
def initialize(config)
|
|
12
|
+
@config = config
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def update
|
|
16
|
+
puts "Updating system and courseware..."
|
|
17
|
+
system("#{@config[:bindir]}/puppet agent -t --confdir #{@config[:confdir]}")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def confirm?(message = 'Continue?', default = true)
|
|
21
|
+
if default
|
|
22
|
+
print "#{message} [Y/n]: "
|
|
23
|
+
return [ 'y', 'yes', '' ].include? STDIN.gets.strip.downcase
|
|
24
|
+
else
|
|
25
|
+
print "#{message} [y/N]: "
|
|
26
|
+
return [ 'y', 'yes' ].include? STDIN.gets.strip.downcase
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def bailout?(message = 'Continue?')
|
|
31
|
+
raise "User cancelled" unless confirm?(message)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def check_success(status=nil)
|
|
35
|
+
status = status.nil? ? ($? == 0) : status
|
|
36
|
+
|
|
37
|
+
if status
|
|
38
|
+
printf("\[\033[32m OK \033[0m]\n")
|
|
39
|
+
else
|
|
40
|
+
printf("[\033[31m FAIL \033[0m]\n")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def help
|
|
45
|
+
require 'classroom/help'
|
|
46
|
+
puts Classroom::HELP
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def debug
|
|
50
|
+
require 'pry'
|
|
51
|
+
binding.pry
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: puppet-classroom-manager
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ben Ford
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-08-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: aws-sdk
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: puppetclassify
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: serverspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: hocon
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rest-client
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
description: |2
|
|
84
|
+
Manage Puppet classroom VMs and updating courseware. This includes
|
|
85
|
+
troubleshooting and maintenance tasks to be used as needed during
|
|
86
|
+
the delivery and facilities for resetting the VM for use across
|
|
87
|
+
multiple deliveries. This is pre-installed on the classroom VM.
|
|
88
|
+
|
|
89
|
+
If you are not teaching Puppet Inc. classes, this is not for you.
|
|
90
|
+
email: education@puppetlabs.com
|
|
91
|
+
executables:
|
|
92
|
+
- classroom
|
|
93
|
+
extensions: []
|
|
94
|
+
extra_rdoc_files: []
|
|
95
|
+
files:
|
|
96
|
+
- README.md
|
|
97
|
+
- LICENSE
|
|
98
|
+
- bin/classroom
|
|
99
|
+
- lib/classroom/help.rb
|
|
100
|
+
- lib/classroom/page.rb
|
|
101
|
+
- lib/classroom/performance.rb
|
|
102
|
+
- lib/classroom/reset.rb
|
|
103
|
+
- lib/classroom/restart.rb
|
|
104
|
+
- lib/classroom/sanitize.rb
|
|
105
|
+
- lib/classroom/submit.rb
|
|
106
|
+
- lib/classroom/troubleshoot.rb
|
|
107
|
+
- lib/classroom/validate.rb
|
|
108
|
+
- lib/classroom/version.rb
|
|
109
|
+
- lib/classroom.rb
|
|
110
|
+
homepage: http://github.com/puppetlabs/puppet-classroom-manager
|
|
111
|
+
licenses:
|
|
112
|
+
- Apache-2.0
|
|
113
|
+
metadata: {}
|
|
114
|
+
post_install_message:
|
|
115
|
+
rdoc_options: []
|
|
116
|
+
require_paths:
|
|
117
|
+
- lib
|
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - ">="
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '0'
|
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
|
+
requirements:
|
|
125
|
+
- - ">="
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: '0'
|
|
128
|
+
requirements: []
|
|
129
|
+
rubyforge_project:
|
|
130
|
+
rubygems_version: 2.0.14.1
|
|
131
|
+
signing_key:
|
|
132
|
+
specification_version: 4
|
|
133
|
+
summary: Manage Puppet classroom VMs and updating courseware.
|
|
134
|
+
test_files: []
|