Skip to content

Commit b0724f3

Browse files
authored
Merge pull request #18 from ggatward/content-automation
Content automation
2 parents 56f13c0 + af6e8ce commit b0724f3

13 files changed

Lines changed: 541 additions & 27 deletions

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
44
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
55

66
## [Unreleased]
7+
### Added
8+
- push_puppetforge now supports jFrog Artifiactory repository via HTTP POST
9+
- sat_import now checks for exports that have not been imported (missed/skipped)
10+
- sat_import --fixhistory option to force align import/export histories
11+
- Email notification capability for use when automating content scripts
12+
- Add unattended option to allow scripts to be automated
13+
- auto_content scripts to allow unattended import/publish/promote/clean activity
14+
15+
### Changed
16+
- --notar export saved in /cdn_export dir rather than /export to prevent it being deleted
17+
18+
### Removed
19+
- Skip GPG short option (-n)
720

821

922
## [1.1.1] - 2017-10-25

README.md

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ hammer user create --login svc-api-user --firstname API --lastname User \
2828
--organization-ids 1 --default-organization-id 1 --admin true
2929
```
3030

31-
Foreman needs to be configured to export content to the location you require. By default the path is
31+
Foreman needs to be configured to export content to the location you require. By default the path is
3232
/var/lib/pulp/katello-export - this will result in you probably filling your /var/lib/pulp partition!
3333
The configs in these scripts assume that the exports are going to /var/sat-export - this should be a
3434
dedicated partition or even better dedicated disk just for export content.
@@ -68,6 +68,11 @@ logging:
6868
dir: /var/log/sat6-scripts (Directory to use for logging)
6969
debug: [True|False]
7070
71+
email:
72+
mailout: True
73+
mailfrom: Satellite 6 <sat62@example.org>
74+
mailto: sysadmin@example.org
75+
7176
export:
7277
dir: /var/sat-export (Directory to export content to - Connected Satellite)
7378
@@ -199,6 +204,7 @@ optional arguments:
199204
-l, --last Display time of last export
200205
-L, --list List all successfully completed exports
201206
--nogpg Skip GPG checking
207+
-u, --unattended Answer any prompts safely, allowing automated usage
202208
-r, --repodata Include repodata for repos with no incremental content
203209
-p, --puppetforge Include puppet-forge-server format Puppet Forge repo
204210
--notar Do not archive the extracted content
@@ -223,11 +229,14 @@ This companion script to sat_export, running on the Disconnected Satellite
223229
performs a sha256sum verification of each part of the specified archive prior
224230
to extracting the transferred content to disk.
225231

226-
Once the content has been extracted, a sync is triggered of each repository
227-
in the import set. Note that repositories MUST be enabled on the disconnected
228-
satellite prior to the sync working - for this reason a `nosync` option (-n)
229-
exists so that the repos can be extracted to disk and then enabled before the
230-
sync occurs. In order to not overload the Satellite during the sync, the
232+
Once the content has been extracted, a check is performed to see if any exports
233+
performed have not yet been imported. This is to assist with data integrity on
234+
the disconnected Satellite system. Any missing imports will be displayed and the
235+
option to continue or abort will be presented. Upon continuing, a sync is triggered
236+
of each repository in the import set. Note that repositories MUST be enabled on the
237+
disconnected satellite prior to the sync working - for this reason a `nosync`
238+
option (-n) exists so that the repos can be extracted to disk and then enabled
239+
before the sync occurs. In order to not overload the Satellite during the sync, the
231240
repositories will be synced in smaller batches, the number of repos in a batch
232241
being defined in the config.yml file. (It has been observed on systems with a
233242
large number of repos that triggering a sync on all repos at once pretty much
@@ -245,6 +254,13 @@ All previously imported datasets can be shown with the (-L) flag.
245254
Note that a dataset can normally only be imported ONCE. To force an import of an
246255
already completed dataset, use the (-f) flag.
247256

257+
In the event that missing import datasets are detected, they should be imported to
258+
ensure data integrity and consistency. There may however be cases that result in
259+
the missing imports being included by other means, or no longer required at all.
260+
In these cases, the --fixhistory flag can be used to 'reset' the import history
261+
so that it matches the export history of the current import dataset, clearing
262+
these warnings.
263+
248264
### Help Output
249265
```
250266
usage: sat_import.py [-h] [-o ORG] -d DATE [-n] [-r] [-l] [-L] [-f]
@@ -260,7 +276,9 @@ optional arguments:
260276
-l, --last Show the last successfully completed import date
261277
-L, --list List all successfully completed imports
262278
-c, --count Display all package counts after import
263-
-f, --force Force import of data if it has previously been done
279+
-f, --force Force import of data if it has previously been done
280+
-u, --unattended Answer any prompts safely, allowing automated usage
281+
--fixhistory Force import history to match export history
264282
```
265283

266284
### Examples
@@ -482,3 +500,14 @@ optional arguments:
482500
./promote_content_view.py -e Production -a # Promote all views to Production
483501
./promote_content_view.py -e Quality -d # See what would be done for Quality
484502
```
503+
504+
505+
# auto_content
506+
Sample script that allows for the unattended automation of content management.
507+
This script will find any import datasets present and import them (in order).
508+
Successful import of the content then triggers a publish. On nominated days/weeks
509+
content is promoted to various lifecycle stages, and content view cleanup is also
510+
performed. Like the other scripts it calls, it supports a dry run (-d) option to
511+
show what would be performed without actually doing it.
512+
513+
This script can be copied and extended to support custom automation requirements.

auto_content.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#!/usr/bin/env python
2+
3+
import sys, os, glob
4+
import subprocess
5+
import argparse
6+
import datetime
7+
import helpers
8+
9+
10+
def dates():
11+
# What day is it? (0=Sun -> 6=Sat)
12+
dayofweek = datetime.datetime.today().weekday()
13+
14+
# Figure out which week of the month we are in
15+
weekofmonth = (datetime.datetime.now().day-1)/7+1
16+
17+
print "Day %s of week %s" % (dayofweek, weekofmonth)
18+
19+
return(dayofweek,weekofmonth)
20+
21+
22+
def run_imports(dryrun):
23+
print "Processing Imports..."
24+
25+
# Find any sha256 files in the import dir
26+
infiles = glob.glob(helpers.IMPORTDIR + '/*.sha256')
27+
28+
# Extract the dataset timestamp/name from the filename and add to a new list
29+
# Assumes naming standard sat6_export_YYYYMMDD-HHMM_NAME.sha256
30+
# 'sorted' function should result in imports being done in correct order by filename
31+
tslist = []
32+
good_imports = False
33+
for f in sorted(infiles):
34+
dstime = f.split('_')[-2]
35+
dsname = (f.split('_')[-1]).split('.')[-2]
36+
tslist.append(dstime + '_' + dsname)
37+
38+
if tslist:
39+
msg = 'Found import datasets on disk...\n' + '\n'.join(tslist)
40+
else:
41+
msg = 'No import datasets to process'
42+
helpers.log_msg(msg, 'INFO')
43+
print msg
44+
45+
# Now for each import file in the list, run the import script in unattended mode:-)
46+
if tslist:
47+
if not dryrun:
48+
for dataset in tslist:
49+
rc = subprocess.call(['/usr/local/bin/sat_import', '-u', '-r', '-d', dataset])
50+
51+
# If the import is successful
52+
if rc == 0:
53+
good_imports = True
54+
55+
else:
56+
msg = "Dry run - not actually performing import"
57+
helpers.log_msg(msg, 'WARNING')
58+
59+
return good_imports
60+
61+
62+
def publish_cv(dryrun):
63+
print "Running Content View Publish..."
64+
65+
# Set the initial state
66+
good_publish = False
67+
68+
if not dryrun:
69+
rc = subprocess.call(['/usr/local/bin/publish_content_views', '-q', '-a'])
70+
else:
71+
msg = "Dry run - not actually performing publish"
72+
helpers.log_msg(msg, 'WARNING')
73+
rc = subprocess.call(['/usr/local/bin/publish_content_views', '-q', '-a', '-d'])
74+
75+
if rc == 0:
76+
good_publish = True
77+
78+
return good_publish
79+
80+
81+
def promote_cv(dryrun, lifecycle):
82+
print "Running Content View Promotion to " + lifecycle + "..."
83+
84+
# Set the initial state
85+
good_promote = False
86+
87+
if not dryrun:
88+
rc = subprocess.call(['/usr/local/bin/promote_content_views', '-q', '-e', lifecycle])
89+
else:
90+
msg = "Dry run - not actually performing promotion"
91+
helpers.log_msg(msg, 'WARNING')
92+
rc = subprocess.call(['/usr/local/bin/promote_content_views', '-q', '-d', '-e', lifecycle])
93+
94+
if rc == 0:
95+
good_promote = True
96+
97+
return good_promote
98+
99+
100+
def push_puppet(dryrun):
101+
print "Pushing puppet modules to puppet-forge server..."
102+
103+
# Set the initial state
104+
good_puppetpush = False
105+
106+
if not dryrun:
107+
for dataset in tslist:
108+
rc = subprocess.call(['/usr/local/bin/push-puppetforge', '-r', 'puppet-forge'])
109+
110+
# If the import is successful
111+
if rc == 0:
112+
good_puppetpush = True
113+
114+
else:
115+
msg = "Dry run - not actually performing module push"
116+
helpers.log_msg(msg, 'WARNING')
117+
118+
return good_puppetpush
119+
120+
121+
def clean_cv(dryrun):
122+
print "Running Content View Cleanup..."
123+
124+
if not dryrun:
125+
rc = subprocess.call(['/usr/local/bin/clean_content_views', '-a', '-c'])
126+
else:
127+
msg = "Dry run - not actually performing cleanup"
128+
helpers.log_msg(msg, 'WARNING')
129+
rc = subprocess.call(['/usr/local/bin/clean_content_views', '-a', '-c', '-d'])
130+
131+
132+
def main(args):
133+
134+
### Run import/publish on scheduled day
135+
136+
# Check for sane input
137+
parser = argparse.ArgumentParser(
138+
description='Imports, Publishes and Promotes content views.')
139+
parser.add_argument('-d', '--dryrun', help='Dry Run - Only show what will be done',
140+
required=False, action="store_true")
141+
parser.add_argument('-p', '--puppet', help='Include puppet-forge module push',
142+
required=False, action="store_true")
143+
144+
args = parser.parse_args()
145+
146+
# Set default flags and read in options given to us
147+
if args.dryrun:
148+
dryrun = True
149+
else:
150+
dryrun = False
151+
152+
run_publish = False
153+
run_promote = True
154+
155+
# Determine the day of week and week of month for use in our scheduling
156+
(dayofweek, weekofmonth) = dates()
157+
158+
159+
# Run promotion first - this ensures content consistency (QA->Prod, Library->QA)
160+
if dayofweek == 1:
161+
if weekofmonth == 4:
162+
run_promote = promote_cv(dryrun, 'Production')
163+
164+
# Run QA promotion on 2nd and 4th Monday. Conditional on Prod promotion success
165+
if weekofmonth == 2 or weekofmonth == 4:
166+
if run_promote:
167+
run_promote = promote_cv(dryrun, 'Quality')
168+
169+
170+
# Every day, check if there are any imports in our input dir and import them.
171+
# run_publish will be returned as 'True' if any successful imports were performed.
172+
# If no imports are performed, or they fail, publish can't be triggered.
173+
run_publish = run_imports(dryrun)
174+
175+
# If the imports succeeded, we can go ahead and publish the new content to Library
176+
if run_publish:
177+
publish_cv(dryrun)
178+
# Push any new puppet-forge modules if we have requested that
179+
if args.puppet:
180+
push_puppet(dryrun)
181+
182+
# Run content view cleanup once a month, after we have done all promotions for the month.
183+
if dayofweek == 4:
184+
if weekofmonth == 4:
185+
clean_cv(dryrun)
186+
187+
188+
if __name__ == "__main__":
189+
try:
190+
main(sys.argv[1:])
191+
except KeyboardInterrupt, e:
192+
print >> sys.stderr, ("\n\nExiting on user cancel.")
193+
sys.exit(1)

bin/auto_content

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/python
2+
import sys
3+
4+
sys.path.insert(0, '/usr/share/sat6_scripts')
5+
try:
6+
import auto_content
7+
auto_content.main(sys.argv[1:])
8+
except KeyboardInterrupt, e:
9+
print >> sys.stderr, "\n\nExiting on user cancel."
10+
sys.exit(1)

config/config.yml.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ logging:
1111
dir: /var/log/satellite
1212
debug: False
1313

14+
email:
15+
mailout: True
16+
mailfrom: Satellite 6 <sat62@example.org>
17+
mailto: sysadmin@example.org
18+
1419
export:
1520
dir: /var/sat-export
1621

0 commit comments

Comments
 (0)