blob: 9984b200803127f7d81d7c6d5f3920864a5a1d97 [file] [log] [blame]
Vasyl Saienko4e8ec642018-09-17 10:08:08 +00001/**
2 *
3 * Run openscap xccdf evaluation on given nodes
4 *
5 * Expected parametes:
6 * SALT_MASTER_URL Full Salt API address.
7 * SALT_MASTER_CREDENTIALS Credentials to the Salt API.
8 *
9 * XCCDF_BENCHMARKS_DIR The XCCDF benchmarks base directory (default /usr/share/xccdf-benchmarks/mirantis/)
10 * XCCDF_BENCHMARKS List of pairs XCCDF benchmark filename and corresponding profile separated with ','
11 * these pairs are separated with semicolon.
12 * (e.g. manila/openstack_manila-xccdf.xml,profilename;horizon/openstack_horizon-xccdf.xml,profile)
13 * XCCDF_VERSION The XCCDF version (default 1.2)
14 * XCCDF_TAILORING_ID The tailoring id (default None)
15 *
16 * TARGET_SERVERS The target Salt nodes (default *)
17 *
18 * ARTIFACTORY_URL The artifactory URL
19 * ARTIFACTORY_NAMESPACE The artifactory namespace (default 'mirantis/openscap')
20 * ARTIFACTORY_REPO The artifactory repo (default 'binary-dev-local')
21 *
22 * UPLOAD_TO_DASHBOARD Boolean. Upload results to the WORP or not
23 * DASHBOARD_API_URL The WORP api base url. Mandatory if UPLOAD_TO_DASHBOARD is true
24 */
25
26
27
28/**
29 * Upload results to the `WORP` dashboard
30 *
31 * @param apiUrl The base dashboard api url
32 * @param cloudName The cloud name (mostly, the given node's domain name)
33 * @param nodeName The node name
34 * @param results The scanning results
35 */
36def uploadResultToDashboard(apiUrl, cloudName, nodeName, results) {
37 // Yes, we do not care of performance and will create at least 4 requests per each result
38 def requestData = [:]
39
40 def cloudId
41 def nodeId
42
43 // Let's take a look, may be our minion is already presented on the dashboard
44 // Get available environments
45 environments = common.parseJSON(http.sendHttpGetRequest("${apiUrl}/environment/"))
46 for (environment in environments) {
47 if (environment['name'] == cloudName) {
48 cloudId = environment['uuid']
49 break
50 }
51 }
52 // Cloud wasn't presented, let's create it
53 if (! cloudId ) {
54 // Create cloud
55 resuestData['name'] = cloudName
56 cloudId = common.parseJSON(http.sendHttpPostRequest("${apiUrl}/environment/", requestData))['env']['uuid']
57
58 // And the node
59 // It was done here to reduce count of requests to the api.
60 // Because if there was not cloud presented on the dashboard, then the node was not presented as well.
61 requestData['nodes'] = [nodeName]
62 nodeId = common.parseJSON(http.sendHttpPutRequest("${apiUrl}/environment/${cloudId}/nodes/", requestData))['uuid']
63 }
64
65 if (! nodeId ) {
66 // Get available nodes in our environment
67 nodes = common.parseJSON(http.sendHttpGetRequest("${apiUrl}/environment/${cloudId}/nodes/"))
68 for (node in nodes) {
69 if (node['name'] == nodeName) {
70 nodeId = node['id']
71 break
72 }
73 }
74 }
75
76 // Node wasn't presented, let's create it
77 if (! nodeId ) {
78 // Create node
79 requestData['nodes'] = [nodeName]
80 nodeId = common.parseJSON(http.sendHttpPutRequest("${apiUrl}/environment/${cloudId}/nodes/", requestData))['uuid']
81 }
82
83 // Get report_id
84 requestData['env_uuid'] = cloudId
85 def reportId = common.parseJSON(http.sendHttpPostRequest("${apiUrl}/reports/openscap/", requestData))['report']['uuid']
86
87 // Upload results
88 requestData['results'] = results
89 requestData['node_name'] = nodeName
90 http.sendHttpPutRequest("${apiUrl}/reports/openscap/${reportId}/", requestData)
91}
92
93
94node('python') {
95 def pepperEnv = 'pepperEnv'
96
97 // XCCDF related variables
98 def benchmarksAndProfilesArray = XCCDF_BENCHMARKS.tokenize(';')
99 def benchmarksDir = XCCDF_BENCHMARKS_DIR ?: '/usr/share/xccdf-benchmarks/mirantis/'
100 def xccdfVersion = XCCDF_VERSION ?: '1.2'
101 def xccdfTailoringId = XCCDF_TAILORING_ID ?: 'None'
102 def targetServers = TARGET_SERVERS ?: '*'
103
104 def salt = new com.mirantis.mk.Salt()
105 def python = new com.mirantis.mk.Python()
106 def common = new com.mirantis.mk.Common()
107 def http = new com.mirantis.mk.Http()
108
109 // To have an ability to work in heavy concurrency conditions
110 def scanUUID = UUID.randomUUID().toString()
111
112 def artifactsArchiveName = "openscap-${scanUUID}.zip"
113 def resultsBaseDir = "/tmp/openscap/${scanUUID}"
114 def artifactsDir = "${env.WORKSPACE}/openscap/${scanUUID}/artifacts"
115
116 def liveMinions
117
118 stage ('Setup virtualenv for Pepper') {
119 python.setupPepperVirtualenv(pepperEnv, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
120 }
121
122 stage ('Run openscap xccdf evaluation and attempt to upload the results to a dashboard') {
123 liveMinions = salt.getMinions(pepperEnv, targetServers)
124
125 if (liveMinions.isEmpty()) {
126 throw new Exception('There are no alive minions')
127 }
128
129 common.infoMsg("Scan UUID: ${scanUUID}")
130
131 for (minion in liveMinions) {
132
133 // Iterate oscap evaluation over the benchmarks
134 for (benchmark in benchmarksAndProfilesArray) {
135 def (benchmarkFilePath, profile) = benchmark.tokenize(',').collect({it.trim()})
136
137 // Remove extension from the benchmark name
138 def benchmarkPathWithoutExtension = benchmarkFilePath.replaceFirst('[.][^.]+$', '')
139 // And build resultsDir based on this path
140 def resultsDir = "${resultsBaseDir}/${benchmarkPathWithoutExtension}"
141
142 def benchmarkFile = "${benchmarksDir}${benchmarkFilePath}"
143
144 // Evaluate the benchmark
145 salt.runSaltProcessStep(pepperEnv, minion, 'oscap.eval', [
146 'xccdf', benchmarkFile, "results_dir=${resultsDir}",
147 "profile=${profile}", "xccdf_version=${xccdfVersion}",
148 "tailoring_id=${xccdfTailoringId}"
149 ])
150
151 // Attempt to upload the scanning results to the dashboard
152 if (UPLOAD_TO_DASHBOARD.toBoolean()) {
153 if (common.validInputParam('DASHBOARD_API_URL')) {
154 def cloudName = salt.getGrain(pepperEnv, minion, 'domain')['return'][0].values()[0].values()[0]
155 uploadResultToDashboard(DASHBOARD_API_URL, cloudName, minion, salt.getFileContent(pepperEnv, minion, "${resultsDir}/results.json"))
156 } else {
157 throw new Exception('Uploading to the dashboard is enabled but the DASHBOARD_API_URL was not set')
158 }
159 }
160 }
161 }
162 }
163
164/* // Will be implemented later
165 stage ('Attempt to upload results to an artifactory') {
166 if (common.validInputParam('ARTIFACTORY_URL')) {
167 for (minion in liveMinions) {
168 def destDir = "${artifactsDir}/${minion}"
169 def archiveName = "openscap-${scanUUID}.tar.gz"
170 def tempArchive = "/tmp/${archiveName}"
171 def destination = "${destDir}/${archiveName}"
172
173 dir(destDir) {
174 // Archive scanning results on the remote target
175 salt.runSaltProcessStep(pepperEnv, minion, 'archive.tar', ['czf', tempArchive, resultsBaseDir])
176
177 // Get it content and save it
178 writeFile file: destination, text: salt.getFileContent(pepperEnv, minion, tempArchive)
179
180 // Remove scanning results and the temp archive on the remote target
181 salt.runSaltProcessStep(pepperEnv, minion, 'file.remove', resultsBaseDir)
182 salt.runSaltProcessStep(pepperEnv, minion, 'file.remove', tempArchive)
183 }
184 }
185
186 def artifactory = new com.mirantis.mcp.MCPArtifactory()
187 def artifactoryName = 'mcp-ci'
188 def artifactoryRepo = ARTIFACTORY_REPO ?: 'binary-dev-local'
189 def artifactoryNamespace = ARTIFACTORY_NAMESPACE ?: 'mirantis/openscap'
190 def artifactoryServer = Artifactory.server(artifactoryName)
191 def publishInfo = true
192 def buildInfo = Artifactory.newBuildInfo()
193 def zipName = "${env.WORKSPACE}/openscap/${scanUUID}/results.zip"
194
195 // Zip scan results
196 zip zipFile: zipName, archive: false, dir: artifactsDir
197
198 // Mandatory and additional properties
199 def properties = artifactory.getBinaryBuildProperties([
200 "scanUuid=${scanUUID}",
201 "project=openscap"
202 ])
203
204 // Build Artifactory spec object
205 def uploadSpec = """{
206 "files":
207 [
208 {
209 "pattern": "${zipName}",
210 "target": "${artifactoryRepo}/${artifactoryNamespace}/openscap",
211 "props": "${properties}"
212 }
213 ]
214 }"""
215
216 // Upload artifacts to the given Artifactory
217 artifactory.uploadBinariesToArtifactory(artifactoryServer, buildInfo, uploadSpec, publishInfo)
218
219 } else {
220 common.warningMsg('ARTIFACTORY_URL was not given, skip uploading to artifactory')
221 }
222 }
223*/
224
225}