Merge branch 'fluorine' into proxy

This commit is contained in:
Daniel Wallace 2018-10-01 17:05:39 -05:00 committed by GitHub
commit 40c6b9d50e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 835 additions and 539 deletions

View File

@ -1,8 +1,11 @@
pipeline { pipeline {
agent { label 'docs' } agent {
label 'docs'
}
options { options {
timestamps() timestamps()
ansiColor('xterm') ansiColor('xterm')
timeout(time: 2, unit: 'HOURS')
} }
environment { environment {
PYENV_ROOT = "/usr/local/pyenv" PYENV_ROOT = "/usr/local/pyenv"

View File

@ -1,73 +1,80 @@
pipeline { timeout(time: 6, unit: 'HOURS') {
agent { label 'kitchen-slave' } node('kitchen-slave') {
options { timestamps {
timestamps() ansiColor('xterm') {
ansiColor('xterm') withEnv([
} 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml',
environment { 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml',
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml" 'PATH=/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin',
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml" 'RBENV_VERSION=2.4.2',
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin" 'TEST_SUITE=py2',
RBENV_VERSION = "2.4.2" 'TEST_PLATFORM=centos-7',
TEST_SUITE = "py2" 'PY_COLORS=1',
TEST_PLATFORM = "centos-7" ]) {
PY_COLORS = 1 stage('checkout-scm') {
} cleanWs notFailBuild: true
stages { checkout scm
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
} }
}} try {
} stage('github-pending') {
post { githubNotify credentialsId: 'test-jenkins-credentials',
always { description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { status: 'PENDING',
sshagent(credentials: ['jenkins-testing-ssh-key']) { context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
} }
}} stage('setup-bundle') {
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml' sh 'bundle install --with ec2 windows --without opennebula docker'
archiveArtifacts artifacts: 'artifacts/logs/minion' }
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log' try {
stage('run kitchen') {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}
}
} finally {
stage('cleanup kitchen') {
script {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}
}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
archiveArtifacts artifacts: 'artifacts/logs/minion'
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log'
}
}
} finally {
try {
junit 'artifacts/xml-unittests-output/*.xml'
} finally {
cleanWs notFailBuild: true
def currentResult = currentBuild.result ?: 'SUCCESS'
if (currentResult == 'SUCCESS') {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
} else {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}
} }
} }
} }
} }
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
} }

View File

@ -1,73 +1,80 @@
pipeline { timeout(time: 6, unit: 'HOURS') {
agent { label 'kitchen-slave' } node('kitchen-slave') {
options { timestamps {
timestamps() ansiColor('xterm') {
ansiColor('xterm') withEnv([
} 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml',
environment { 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml',
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml" 'PATH=/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin',
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml" 'RBENV_VERSION=2.4.2',
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin" 'TEST_SUITE=py3',
RBENV_VERSION = "2.4.2" 'TEST_PLATFORM=centos-7',
TEST_SUITE = "py3" 'PY_COLORS=1',
TEST_PLATFORM = "centos-7" ]) {
PY_COLORS = 1 stage('checkout-scm') {
} cleanWs notFailBuild: true
stages { checkout scm
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
} }
}} try {
} stage('github-pending') {
post { githubNotify credentialsId: 'test-jenkins-credentials',
always { description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { status: 'PENDING',
sshagent(credentials: ['jenkins-testing-ssh-key']) { context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
} }
}} stage('setup-bundle') {
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml' sh 'bundle install --with ec2 windows --without opennebula docker'
archiveArtifacts artifacts: 'artifacts/logs/minion' }
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log' try {
stage('run kitchen') {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}
}
} finally {
stage('cleanup kitchen') {
script {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}
}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
archiveArtifacts artifacts: 'artifacts/logs/minion'
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log'
}
}
} finally {
try {
junit 'artifacts/xml-unittests-output/*.xml'
} finally {
cleanWs notFailBuild: true
def currentResult = currentBuild.result ?: 'SUCCESS'
if (currentResult == 'SUCCESS') {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
} else {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}
} }
} }
} }
} }
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
} }

View File

@ -1,73 +1,80 @@
pipeline { timeout(time: 6, unit: 'HOURS') {
agent { label 'kitchen-slave' } node('kitchen-slave') {
options { timestamps {
timestamps() ansiColor('xterm') {
ansiColor('xterm') withEnv([
} 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml',
environment { 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml',
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml" 'PATH=/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin',
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml" 'RBENV_VERSION=2.4.2',
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin" 'TEST_SUITE=py2',
RBENV_VERSION = "2.4.2" 'TEST_PLATFORM=ubuntu-1604',
TEST_SUITE = "py2" 'PY_COLORS=1',
TEST_PLATFORM = "ubuntu-1604" ]) {
PY_COLORS = 1 stage('checkout-scm') {
} cleanWs notFailBuild: true
stages { checkout scm
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
} }
}} try {
} stage('github-pending') {
post { githubNotify credentialsId: 'test-jenkins-credentials',
always { description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { status: 'PENDING',
sshagent(credentials: ['jenkins-testing-ssh-key']) { context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
} }
}} stage('setup-bundle') {
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml' sh 'bundle install --with ec2 windows --without opennebula docker'
archiveArtifacts artifacts: 'artifacts/logs/minion' }
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log' try {
stage('run kitchen') {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}
}
} finally {
stage('cleanup kitchen') {
script {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}
}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
archiveArtifacts artifacts: 'artifacts/logs/minion'
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log'
}
}
} finally {
try {
junit 'artifacts/xml-unittests-output/*.xml'
} finally {
cleanWs notFailBuild: true
def currentResult = currentBuild.result ?: 'SUCCESS'
if ( currentResult == 'SUCCESS') {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
} else {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}
} }
} }
} }
} }
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
} }

View File

@ -1,73 +1,80 @@
pipeline { timeout(time: 6, unit: 'HOURS') {
agent { label 'kitchen-slave' } node('kitchen-slave') {
options { timestamps {
timestamps() ansiColor('xterm') {
ansiColor('xterm') withEnv([
} 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml',
environment { 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml',
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml" 'PATH=/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin',
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml" 'RBENV_VERSION=2.4.2',
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin" 'TEST_SUITE=py3',
RBENV_VERSION = "2.4.2" 'TEST_PLATFORM=ubuntu-1604',
TEST_SUITE = "py3" 'PY_COLORS=1',
TEST_PLATFORM = "ubuntu-1604" ]) {
PY_COLORS = 1 stage('checkout-scm') {
} cleanWs notFailBuild: true
stages { checkout scm
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
} }
}} try {
} stage('github-pending') {
post { githubNotify credentialsId: 'test-jenkins-credentials',
always { description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { status: 'PENDING',
sshagent(credentials: ['jenkins-testing-ssh-key']) { context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
} }
}} stage('setup-bundle') {
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml' sh 'bundle install --with ec2 windows --without opennebula docker'
archiveArtifacts artifacts: 'artifacts/logs/minion' }
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log' try {
stage('run kitchen') {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}
}
} finally {
stage('cleanup kitchen') {
script {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}
}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
archiveArtifacts artifacts: 'artifacts/logs/minion'
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log'
}
}
} finally {
try {
junit 'artifacts/xml-unittests-output/*.xml'
} finally {
cleanWs notFailBuild: true
def currentResult = currentBuild.result ?: 'SUCCESS'
if (currentResult == 'SUCCESS') {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
} else {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}
} }
} }
} }
} }
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
} }

View File

@ -1,73 +1,80 @@
pipeline { timeout(time: 6, unit: 'HOURS') {
agent { label 'kitchen-slave' } node('kitchen-slave') {
options { timestamps {
timestamps() ansiColor('xterm') {
ansiColor('xterm') withEnv([
} 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml',
environment { 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml',
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml" 'PATH=/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin',
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml" 'RBENV_VERSION=2.4.2',
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin" 'TEST_SUITE=py2',
RBENV_VERSION = "2.4.2" 'TEST_PLATFORM=windows-2016',
TEST_SUITE = "py2" 'PY_COLORS=1',
TEST_PLATFORM = "windows-2016" ]) {
PY_COLORS = 1 stage('checkout-scm') {
} cleanWs notFailBuild: true
stages { checkout scm
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
} }
}} try {
} stage('github-pending') {
post { githubNotify credentialsId: 'test-jenkins-credentials',
always { description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { status: 'PENDING',
sshagent(credentials: ['jenkins-testing-ssh-key']) { context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
} }
}} stage('setup-bundle') {
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml' sh 'bundle install --with ec2 windows --without opennebula docker'
archiveArtifacts artifacts: 'artifacts/logs/minion' }
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log' try {
stage('run kitchen') {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}
}
} finally {
stage('cleanup kitchen') {
script {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}
}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
archiveArtifacts artifacts: 'artifacts/logs/minion'
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log'
}
}
} finally {
try {
junit 'artifacts/xml-unittests-output/*.xml'
} finally {
cleanWs notFailBuild: true
def currentResult = currentBuild.result ?: 'SUCCESS'
if (currentResult == 'SUCCESS') {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
} else {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}
} }
} }
} }
} }
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
} }

View File

@ -1,73 +1,80 @@
pipeline { timeout(time: 6, unit: 'HOURS') {
agent { label 'kitchen-slave' } node('kitchen-slave') {
options { timestamps {
timestamps() ansiColor('xterm') {
ansiColor('xterm') withEnv([
} 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml',
environment { 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml',
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml" 'PATH=/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin',
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml" 'RBENV_VERSION=2.4.2',
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin" 'TEST_SUITE=py3',
RBENV_VERSION = "2.4.2" 'TEST_PLATFORM=windows-2016',
TEST_SUITE = "py3" 'PY_COLORS=1',
TEST_PLATFORM = "windows-2016" ]) {
PY_COLORS = 1 stage('checkout-scm') {
} cleanWs notFailBuild: true
stages { checkout scm
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
} }
}} try {
} stage('github-pending') {
post { githubNotify credentialsId: 'test-jenkins-credentials',
always { description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { status: 'PENDING',
sshagent(credentials: ['jenkins-testing-ssh-key']) { context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
} }
}} stage('setup-bundle') {
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml' sh 'bundle install --with ec2 windows --without opennebula docker'
archiveArtifacts artifacts: 'artifacts/logs/minion' }
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log' try {
stage('run kitchen') {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}
}
} finally {
stage('cleanup kitchen') {
script {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']
]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}
}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
archiveArtifacts artifacts: 'artifacts/logs/minion'
archiveArtifacts artifacts: 'artifacts/logs/salt-runtests.log'
}
}
} finally {
try {
junit 'artifacts/xml-unittests-output/*.xml'
} finally {
cleanWs notFailBuild: true
def currentResult = currentBuild.result ?: 'SUCCESS'
if (currentResult == 'SUCCESS') {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
} else {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}
} }
} }
} }
} }
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
} }

View File

@ -3,6 +3,7 @@ pipeline {
options { options {
timestamps() timestamps()
ansiColor('xterm') ansiColor('xterm')
timeout(time: 1, unit: 'HOURS')
} }
environment { environment {
PYENV_ROOT = "/usr/local/pyenv" PYENV_ROOT = "/usr/local/pyenv"

View File

@ -2,18 +2,21 @@
''' '''
Salt compatibility code Salt compatibility code
''' '''
# pylint: disable=import-error,unused-import,invalid-name # pylint: disable=import-error,unused-import,invalid-name,W0231,W0233
# Import python libs # Import python libs
from __future__ import absolute_import from __future__ import absolute_import, unicode_literals, print_function
import sys import sys
import types import types
import logging
# Import 3rd-party libs # Import 3rd-party libs
from salt.ext.six import binary_type, string_types, text_type from salt.exceptions import SaltException
from salt.ext.six import binary_type, string_types, text_type, integer_types
from salt.ext.six.moves import cStringIO, StringIO from salt.ext.six.moves import cStringIO, StringIO
HAS_XML = True log = logging.getLogger(__name__)
try: try:
# Python >2.5 # Python >2.5
import xml.etree.cElementTree as ElementTree import xml.etree.cElementTree as ElementTree
@ -31,11 +34,10 @@ except Exception:
import elementtree.ElementTree as ElementTree import elementtree.ElementTree as ElementTree
except Exception: except Exception:
ElementTree = None ElementTree = None
HAS_XML = False
# True if we are running on Python 3. # True if we are running on Python 3.
PY3 = sys.version_info[0] == 3 PY3 = sys.version_info.major == 3
if PY3: if PY3:
@ -45,13 +47,12 @@ else:
import exceptions import exceptions
if HAS_XML: if ElementTree is not None:
if not hasattr(ElementTree, 'ParseError'): if not hasattr(ElementTree, 'ParseError'):
class ParseError(Exception): class ParseError(Exception):
''' '''
older versions of ElementTree do not have ParseError older versions of ElementTree do not have ParseError
''' '''
pass
ElementTree.ParseError = ParseError ElementTree.ParseError = ParseError
@ -61,9 +62,7 @@ def text_(s, encoding='latin-1', errors='strict'):
If ``s`` is an instance of ``binary_type``, return If ``s`` is an instance of ``binary_type``, return
``s.decode(encoding, errors)``, otherwise return ``s`` ``s.decode(encoding, errors)``, otherwise return ``s``
''' '''
if isinstance(s, binary_type): return s.decode(encoding, errors) if isinstance(s, binary_type) else s
return s.decode(encoding, errors)
return s
def bytes_(s, encoding='latin-1', errors='strict'): def bytes_(s, encoding='latin-1', errors='strict'):
@ -71,57 +70,37 @@ def bytes_(s, encoding='latin-1', errors='strict'):
If ``s`` is an instance of ``text_type``, return If ``s`` is an instance of ``text_type``, return
``s.encode(encoding, errors)``, otherwise return ``s`` ``s.encode(encoding, errors)``, otherwise return ``s``
''' '''
return s.encode(encoding, errors) if isinstance(s, text_type) else s
def ascii_native_(s):
'''
Python 3: If ``s`` is an instance of ``text_type``, return
``s.encode('ascii')``, otherwise return ``str(s, 'ascii', 'strict')``
Python 2: If ``s`` is an instance of ``text_type``, return
``s.encode('ascii')``, otherwise return ``str(s)``
'''
if isinstance(s, text_type): if isinstance(s, text_type):
return s.encode(encoding, errors) s = s.encode('ascii')
return s
return str(s, 'ascii', 'strict') if PY3 else s
if PY3: def native_(s, encoding='latin-1', errors='strict'):
def ascii_native_(s): '''
if isinstance(s, text_type): Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise
s = s.encode('ascii') return ``str(s, encoding, errors)``
return str(s, 'ascii', 'strict')
else:
def ascii_native_(s):
if isinstance(s, text_type):
s = s.encode('ascii')
return str(s)
ascii_native_.__doc__ = ''' Python 2: If ``s`` is an instance of ``text_type``, return
Python 3: If ``s`` is an instance of ``text_type``, return ``s.encode(encoding, errors)``, otherwise return ``str(s)``
``s.encode('ascii')``, otherwise return ``str(s, 'ascii', 'strict')`` '''
if PY3:
out = s if isinstance(s, text_type) else str(s, encoding, errors)
else:
out = s.encode(encoding, errors) if isinstance(s, text_type) else str(s)
Python 2: If ``s`` is an instance of ``text_type``, return return out
``s.encode('ascii')``, otherwise return ``str(s)``
'''
if PY3:
def native_(s, encoding='latin-1', errors='strict'):
'''
If ``s`` is an instance of ``text_type``, return
``s``, otherwise return ``str(s, encoding, errors)``
'''
if isinstance(s, text_type):
return s
return str(s, encoding, errors)
else:
def native_(s, encoding='latin-1', errors='strict'):
'''
If ``s`` is an instance of ``text_type``, return
``s.encode(encoding, errors)``, otherwise return ``str(s)``
'''
if isinstance(s, text_type):
return s.encode(encoding, errors)
return str(s)
native_.__doc__ = '''
Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise
return ``str(s, encoding, errors)``
Python 2: If ``s`` is an instance of ``text_type``, return
``s.encode(encoding, errors)``, otherwise return ``str(s)``
'''
def string_io(data=None): # cStringIO can't handle unicode def string_io(data=None): # cStringIO can't handle unicode
@ -133,7 +112,199 @@ def string_io(data=None): # cStringIO can't handle unicode
except (UnicodeEncodeError, TypeError): except (UnicodeEncodeError, TypeError):
return StringIO(data) return StringIO(data)
if PY3:
import ipaddress try:
else: if PY3:
import salt.ext.ipaddress as ipaddress import ipaddress
else:
import salt.ext.ipaddress as ipaddress
except ImportError:
ipaddress = None
class IPv6AddressScoped(ipaddress.IPv6Address):
'''
Represent and manipulate single IPv6 Addresses.
Scope-aware version
'''
def __init__(self, address):
'''
Instantiate a new IPv6 address object. Scope is moved to an attribute 'scope'.
Args:
address: A string or integer representing the IP
Additionally, an integer can be passed, so
IPv6Address('2001:db8::') == IPv6Address(42540766411282592856903984951653826560)
or, more generally
IPv6Address(int(IPv6Address('2001:db8::'))) == IPv6Address('2001:db8::')
Raises:
AddressValueError: If address isn't a valid IPv6 address.
:param address:
'''
# pylint: disable-all
if not hasattr(self, '_is_packed_binary'):
# This method (below) won't be around for some Python 3 versions
# and we need check this differently anyway
self._is_packed_binary = lambda p: isinstance(p, bytes)
# pylint: enable-all
if isinstance(address, string_types) and '%' in address:
buff = address.split('%')
if len(buff) != 2:
raise SaltException('Invalid IPv6 address: "{}"'.format(address))
address, self.__scope = buff
else:
self.__scope = None
if sys.version_info.major == 2:
ipaddress._BaseAddress.__init__(self, address)
ipaddress._BaseV6.__init__(self, address)
else:
# Python 3.4 fix. Versions higher are simply not affected
# https://github.com/python/cpython/blob/3.4/Lib/ipaddress.py#L543-L544
self._version = 6
self._max_prefixlen = ipaddress.IPV6LENGTH
# Efficient constructor from integer.
if isinstance(address, integer_types):
self._check_int_address(address)
self._ip = address
elif self._is_packed_binary(address):
self._check_packed_address(address, 16)
self._ip = ipaddress._int_from_bytes(address, 'big')
else:
address = str(address)
if '/' in address:
raise ipaddress.AddressValueError("Unexpected '/' in {}".format(address))
self._ip = self._ip_int_from_string(address)
def _is_packed_binary(self, data):
'''
Check if data is hexadecimal packed
:param data:
:return:
'''
packed = False
if len(data) == 16 and ':' not in data:
try:
packed = bool(int(str(bytearray(data)).encode('hex'), 16))
except ValueError:
pass
return packed
@property
def scope(self):
'''
Return scope of IPv6 address.
:return:
'''
return self.__scope
def __str__(self):
return text_type(self._string_from_ip_int(self._ip) +
('%' + self.scope if self.scope is not None else ''))
class IPv6InterfaceScoped(ipaddress.IPv6Interface, IPv6AddressScoped):
'''
Update
'''
def __init__(self, address):
if isinstance(address, (bytes, int)):
IPv6AddressScoped.__init__(self, address)
self.network = ipaddress.IPv6Network(self._ip)
self._prefixlen = self._max_prefixlen
return
addr = ipaddress._split_optional_netmask(address)
IPv6AddressScoped.__init__(self, addr[0])
self.network = ipaddress.IPv6Network(address, strict=False)
self.netmask = self.network.netmask
self._prefixlen = self.network._prefixlen
self.hostmask = self.network.hostmask
def ip_address(address):
"""Take an IP string/int and return an object of the correct type.
Args:
address: A string or integer, the IP address. Either IPv4 or
IPv6 addresses may be supplied; integers less than 2**32 will
be considered to be IPv4 by default.
Returns:
An IPv4Address or IPv6Address object.
Raises:
ValueError: if the *address* passed isn't either a v4 or a v6
address
"""
try:
return ipaddress.IPv4Address(address)
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
log.debug('Error while parsing IPv4 address: %s', address)
log.debug(err)
try:
return IPv6AddressScoped(address)
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
log.debug('Error while parsing IPv6 address: %s', address)
log.debug(err)
if isinstance(address, bytes):
raise ipaddress.AddressValueError('{} does not appear to be an IPv4 or IPv6 address. '
'Did you pass in a bytes (str in Python 2) instead '
'of a unicode object?'.format(repr(address)))
raise ValueError('{} does not appear to be an IPv4 or IPv6 address'.format(repr(address)))
def ip_interface(address):
"""Take an IP string/int and return an object of the correct type.
Args:
address: A string or integer, the IP address. Either IPv4 or
IPv6 addresses may be supplied; integers less than 2**32 will
be considered to be IPv4 by default.
Returns:
An IPv4Interface or IPv6Interface object.
Raises:
ValueError: if the string passed isn't either a v4 or a v6
address.
Notes:
The IPv?Interface classes describe an Address on a particular
Network, so they're basically a combination of both the Address
and Network classes.
"""
try:
return ipaddress.IPv4Interface(address)
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
log.debug('Error while getting IPv4 interface for address %s', address)
log.debug(err)
try:
return ipaddress.IPv6Interface(address)
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
log.debug('Error while getting IPv6 interface for address %s', address)
log.debug(err)
raise ValueError('{} does not appear to be an IPv4 or IPv6 interface'.format(address))
if ipaddress:
ipaddress.IPv6Address = IPv6AddressScoped
if sys.version_info.major == 2:
ipaddress.IPv6Interface = IPv6InterfaceScoped
ipaddress.ip_address = ip_address
ipaddress.ip_interface = ip_interface

View File

@ -27,10 +27,7 @@ import salt.utils.cloud
import salt.config as config import salt.config as config
import salt.client import salt.client
import salt.ext.six as six import salt.ext.six as six
if six.PY3: from salt._compat import ipaddress
import ipaddress
else:
import salt.ext.ipaddress as ipaddress
from salt.exceptions import SaltCloudException, SaltCloudSystemExit from salt.exceptions import SaltCloudException, SaltCloudSystemExit

View File

@ -25,13 +25,8 @@ import tempfile
import salt.utils import salt.utils
import salt.config as config import salt.config as config
import salt.client import salt.client
import salt.ext.six as six from salt._compat import ipaddress
if six.PY3: from salt.exceptions import SaltCloudException, SaltCloudSystemExit, SaltInvocationError
import ipaddress
else:
import salt.ext.ipaddress as ipaddress
from salt.exceptions import SaltCloudException, SaltCloudSystemExit, \
SaltInvocationError
# Get logging started # Get logging started
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -9,7 +9,7 @@ from __future__ import absolute_import
import socket import socket
import ctypes import ctypes
import os import os
import ipaddress from salt._compat import ipaddress
import salt.ext.six as six import salt.ext.six as six

View File

@ -5,9 +5,11 @@ Generate chronos proxy minion grains.
.. versionadded:: 2015.8.2 .. versionadded:: 2015.8.2
''' '''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
# Import Salt libs
import salt.utils.http import salt.utils.http
import salt.utils.platform import salt.utils.platform
__proxyenabled__ = ['chronos'] __proxyenabled__ = ['chronos']

View File

@ -113,13 +113,16 @@ __virtualname__ = 'sentry'
def __virtual__(): def __virtual__():
if HAS_RAVEN is True: if HAS_RAVEN is True:
__grains__ = salt.loader.grains(__opts__)
__salt__ = salt.loader.minion_mods(__opts__)
return __virtualname__ return __virtualname__
return False return False
def setup_handlers(): def setup_handlers():
'''
sets up the sentry handler
'''
__grains__ = salt.loader.grains(__opts__)
__salt__ = salt.loader.minion_mods(__opts__)
if 'sentry_handler' not in __opts__: if 'sentry_handler' not in __opts__:
log.debug('No \'sentry_handler\' key was found in the configuration') log.debug('No \'sentry_handler\' key was found in the configuration')
return False return False
@ -133,7 +136,9 @@ def setup_handlers():
transport_registry = TransportRegistry(default_transports) transport_registry = TransportRegistry(default_transports)
url = urlparse(dsn) url = urlparse(dsn)
if not transport_registry.supported_scheme(url.scheme): if not transport_registry.supported_scheme(url.scheme):
raise ValueError('Unsupported Sentry DSN scheme: {0}'.format(url.scheme)) raise ValueError(
'Unsupported Sentry DSN scheme: %s', url.scheme
)
except ValueError as exc: except ValueError as exc:
log.info( log.info(
'Raven failed to parse the configuration provided DSN: %s', exc 'Raven failed to parse the configuration provided DSN: %s', exc
@ -202,7 +207,11 @@ def setup_handlers():
context_dict = {} context_dict = {}
if context is not None: if context is not None:
for tag in context: for tag in context:
tag_value = __salt__['grains.get'](tag) try:
tag_value = __grains__[tag]
except KeyError:
log.debug('Sentry tag \'%s\' not found in grains.', tag)
continue
if len(tag_value) > 0: if len(tag_value) > 0:
context_dict[tag] = tag_value context_dict[tag] = tag_value
if len(context_dict) > 0: if len(context_dict) > 0:
@ -229,4 +238,7 @@ def setup_handlers():
def get_config_value(name, default=None): def get_config_value(name, default=None):
'''
returns a configuration option for the sentry_handler
'''
return __opts__['sentry_handler'].get(name, default) return __opts__['sentry_handler'].get(name, default)

View File

@ -13,10 +13,7 @@ from salt.ext.six.moves import map, range
import salt.utils.path import salt.utils.path
# Import third-party libs # Import third-party libs
if six.PY3: from salt._compat import ipaddress
import ipaddress
else:
import salt.ext.ipaddress as ipaddress
# Set up logging # Set up logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -26,10 +26,7 @@ from salt.exceptions import CommandExecutionError
# Import 3rd-party libs # Import 3rd-party libs
from salt.ext import six from salt.ext import six
from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin
if six.PY3: from salt._compat import ipaddress
import ipaddress
else:
import salt.ext.ipaddress as ipaddress
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -39,11 +39,7 @@ import salt.utils.path
import salt.utils.stringutils import salt.utils.stringutils
from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.exceptions import CommandExecutionError, SaltInvocationError
import salt.ext.six as six import salt.ext.six as six
from salt._compat import ipaddress
if six.PY3:
import ipaddress
else:
import salt.ext.ipaddress as ipaddress
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -76,7 +76,25 @@ class _Zypper(object):
Allows serial zypper calls (first came, first won). Allows serial zypper calls (first came, first won).
''' '''
SUCCESS_EXIT_CODES = [0, 100, 101, 102, 103] SUCCESS_EXIT_CODES = {
0: 'Successful run of zypper with no special info.',
100: 'Patches are available for installation.',
101: 'Security patches are available for installation.',
102: 'Installation successful, reboot required.',
103: 'Installation succesful, restart of the package manager itself required.',
}
WARNING_EXIT_CODES = {
6: 'No repositories are defined.',
7: 'The ZYPP library is locked.',
106: 'Some repository had to be disabled temporarily because it failed to refresh. '
'You should check your repository configuration (e.g. zypper ref -f).',
107: 'Installation basically succeeded, but some of the packages %post install scripts returned an error. '
'These packages were successfully unpacked to disk and are registered in the rpm database, '
'but due to the failed install script they may not work as expected. The failed scripts output might '
'reveal what actually went wrong. Any scripts output is also logged to /var/log/zypp/history.'
}
LOCK_EXIT_CODE = 7 LOCK_EXIT_CODE = 7
XML_DIRECTIVES = ['-x', '--xmlout'] XML_DIRECTIVES = ['-x', '--xmlout']
ZYPPER_LOCK = '/var/run/zypp.pid' ZYPPER_LOCK = '/var/run/zypp.pid'
@ -189,7 +207,15 @@ class _Zypper(object):
:return: :return:
''' '''
return self.exit_code not in self.SUCCESS_EXIT_CODES if self.exit_code:
msg = self.SUCCESS_EXIT_CODES.get(self.exit_code)
if msg:
log.info(msg)
msg = self.WARNING_EXIT_CODES.get(self.exit_code)
if msg:
log.warning(msg)
return self.exit_code not in self.SUCCESS_EXIT_CODES and self.exit_code not in self.WARNING_EXIT_CODES
def _is_lock(self): def _is_lock(self):
''' '''

View File

@ -10,6 +10,7 @@ import logging
import salt.loader import salt.loader
import salt.utils.event import salt.utils.event
import salt.utils.functools import salt.utils.functools
import salt.utils.jid
from salt.exceptions import SaltInvocationError from salt.exceptions import SaltInvocationError
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -110,6 +111,8 @@ def orchestrate(mods,
pillarenv = __opts__['pillarenv'] pillarenv = __opts__['pillarenv']
if saltenv is None and 'saltenv' in __opts__: if saltenv is None and 'saltenv' in __opts__:
saltenv = __opts__['saltenv'] saltenv = __opts__['saltenv']
if orchestration_jid is None:
orchestration_jid = salt.utils.jid.gen_jid(__opts__)
running = minion.functions['state.sls']( running = minion.functions['state.sls'](
mods, mods,

View File

@ -5215,7 +5215,9 @@ def append(name,
check_res, check_msg = _check_file(name) check_res, check_msg = _check_file(name)
if not check_res: if not check_res:
# Try to create the file # Try to create the file
touch(name, makedirs=makedirs) touch_ret = touch(name, makedirs=makedirs)
if __opts__['test']:
return touch_ret
retry_res, retry_msg = _check_file(name) retry_res, retry_msg = _check_file(name)
if not retry_res: if not retry_res:
return _error(ret, check_msg) return _error(ret, check_msg)
@ -5496,7 +5498,9 @@ def prepend(name,
check_res, check_msg = _check_file(name) check_res, check_msg = _check_file(name)
if not check_res: if not check_res:
# Try to create the file # Try to create the file
touch(name, makedirs=makedirs) touch_ret = touch(name, makedirs=makedirs)
if __opts__['test']:
return touch_ret
retry_res, retry_msg = _check_file(name) retry_res, retry_msg = _check_file(name)
if not retry_res: if not retry_res:
return _error(ret, check_msg) return _error(ret, check_msg)

View File

@ -455,13 +455,14 @@ def traverse_dict(data, key, default=None, delimiter=DEFAULT_TARGET_DELIM):
data['foo']['bar']['baz'] if this value exists, and will otherwise return data['foo']['bar']['baz'] if this value exists, and will otherwise return
the dict in the default argument. the dict in the default argument.
''' '''
ptr = data
try: try:
for each in key.split(delimiter): for each in key.split(delimiter):
data = data[each] ptr = ptr[each]
except (KeyError, IndexError, TypeError): except (KeyError, IndexError, TypeError):
# Encountered a non-indexable value in the middle of traversing # Encountered a non-indexable value in the middle of traversing
return default return default
return data return ptr
@jinja_filter('traverse') @jinja_filter('traverse')
@ -476,16 +477,17 @@ def traverse_dict_and_list(data, key, default=None, delimiter=DEFAULT_TARGET_DEL
{'foo':{'bar':['baz']}} , if data like {'foo':{'bar':{'0':'baz'}}} {'foo':{'bar':['baz']}} , if data like {'foo':{'bar':{'0':'baz'}}}
then return data['foo']['bar']['0'] then return data['foo']['bar']['0']
''' '''
ptr = data
for each in key.split(delimiter): for each in key.split(delimiter):
if isinstance(data, list): if isinstance(ptr, list):
try: try:
idx = int(each) idx = int(each)
except ValueError: except ValueError:
embed_match = False embed_match = False
# Index was not numeric, lets look at any embedded dicts # Index was not numeric, lets look at any embedded dicts
for embedded in (x for x in data if isinstance(x, dict)): for embedded in (x for x in ptr if isinstance(x, dict)):
try: try:
data = embedded[each] ptr = embedded[each]
embed_match = True embed_match = True
break break
except KeyError: except KeyError:
@ -495,15 +497,15 @@ def traverse_dict_and_list(data, key, default=None, delimiter=DEFAULT_TARGET_DEL
return default return default
else: else:
try: try:
data = data[idx] ptr = ptr[idx]
except IndexError: except IndexError:
return default return default
else: else:
try: try:
data = data[each] ptr = ptr[each]
except (KeyError, TypeError): except (KeyError, TypeError):
return default return default
return data return ptr
def subdict_match(data, def subdict_match(data,
@ -519,16 +521,33 @@ def subdict_match(data,
former, as more deeply-nested matches are tried first. former, as more deeply-nested matches are tried first.
''' '''
def _match(target, pattern, regex_match=False, exact_match=False): def _match(target, pattern, regex_match=False, exact_match=False):
# The reason for using six.text_type first and _then_ using
# to_unicode as a fallback is because we want to eventually have
# unicode types for comparison below. If either value is numeric then
# six.text_type will turn it into a unicode string. However, if the
# value is a PY2 str type with non-ascii chars, then the result will be
# a UnicodeDecodeError. In those cases, we simply use to_unicode to
# decode it to unicode. The reason we can't simply use to_unicode to
# begin with is that (by design) to_unicode will raise a TypeError if a
# non-string/bytestring/bytearray value is passed.
try:
target = six.text_type(target).lower()
except UnicodeDecodeError:
target = salt.utils.stringutils.to_unicode(target).lower()
try:
pattern = six.text_type(pattern).lower()
except UnicodeDecodeError:
pattern = salt.utils.stringutils.to_unicode(pattern).lower()
if regex_match: if regex_match:
try: try:
return re.match(pattern.lower(), six.text_type(target).lower()) return re.match(pattern, target)
except Exception: except Exception:
log.error('Invalid regex \'%s\' in match', pattern) log.error('Invalid regex \'%s\' in match', pattern)
return False return False
elif exact_match:
return six.text_type(target).lower() == pattern.lower()
else: else:
return fnmatch.fnmatch(six.text_type(target).lower(), pattern.lower()) return target == pattern if exact_match \
else fnmatch.fnmatch(target, pattern)
def _dict_match(target, pattern, regex_match=False, exact_match=False): def _dict_match(target, pattern, regex_match=False, exact_match=False):
wildcard = pattern.startswith('*:') wildcard = pattern.startswith('*:')
@ -548,11 +567,6 @@ def subdict_match(data,
return True return True
if wildcard: if wildcard:
for key in target: for key in target:
if _match(key,
pattern,
regex_match=regex_match,
exact_match=exact_match):
return True
if isinstance(target[key], dict): if isinstance(target[key], dict):
if _dict_match(target[key], if _dict_match(target[key],
pattern, pattern,
@ -566,6 +580,17 @@ def subdict_match(data,
regex_match=regex_match, regex_match=regex_match,
exact_match=exact_match): exact_match=exact_match):
return True return True
elif _match(target[key],
pattern,
regex_match=regex_match,
exact_match=exact_match):
return True
return False
splits = expr.split(delimiter)
num_splits = len(splits)
if num_splits == 1:
# Delimiter not present, this can't possibly be a match
return False return False
splits = expr.split(delimiter) splits = expr.split(delimiter)
@ -578,10 +603,16 @@ def subdict_match(data,
# want to use are 3, 2, and 1, in that order. # want to use are 3, 2, and 1, in that order.
for idx in range(num_splits - 1, 0, -1): for idx in range(num_splits - 1, 0, -1):
key = delimiter.join(splits[:idx]) key = delimiter.join(splits[:idx])
matchstr = delimiter.join(splits[idx:]) if key == '*':
# We are matching on everything under the top level, so we need to
# treat the match as the entire data being passed in
matchstr = expr
match = data
else:
matchstr = delimiter.join(splits[idx:])
match = traverse_dict_and_list(data, key, {}, delimiter=delimiter)
log.debug("Attempting to match '%s' in '%s' using delimiter '%s'", log.debug("Attempting to match '%s' in '%s' using delimiter '%s'",
matchstr, key, delimiter) matchstr, key, delimiter)
match = traverse_dict_and_list(data, key, {}, delimiter=delimiter)
if match == {}: if match == {}:
continue continue
if isinstance(match, dict): if isinstance(match, dict):

View File

@ -1139,18 +1139,13 @@ def parse_resolv(src='/etc/resolv.conf'):
try: try:
(directive, arg) = (line[0].lower(), line[1:]) (directive, arg) = (line[0].lower(), line[1:])
# Drop everything after # or ; (comments) # Drop everything after # or ; (comments)
arg = list(itertools.takewhile( arg = list(itertools.takewhile(lambda x: x[0] not in ('#', ';'), arg))
lambda x: x[0] not in ('#', ';'), arg))
if directive == 'nameserver': if directive == 'nameserver':
# Split the scope (interface) if it is present addr = arg[0]
addr, scope = arg[0].split('%', 1) if '%' in arg[0] else (arg[0], '')
try: try:
ip_addr = ipaddress.ip_address(addr) ip_addr = ipaddress.ip_address(addr)
version = ip_addr.version version = ip_addr.version
# Rejoin scope after address validation ip_addr = str(ip_addr)
if scope:
ip_addr = '%'.join((str(ip_addr), scope))
if ip_addr not in nameservers: if ip_addr not in nameservers:
nameservers.append(ip_addr) nameservers.append(ip_addr)
if version == 4 and ip_addr not in ip4_nameservers: if version == 4 and ip_addr not in ip4_nameservers:

View File

@ -26,10 +26,7 @@ import salt.cache
from salt.ext import six from salt.ext import six
# Import 3rd-party libs # Import 3rd-party libs
if six.PY3: from salt._compat import ipaddress
import ipaddress
else:
import salt.ext.ipaddress as ipaddress
HAS_RANGE = False HAS_RANGE = False
try: try:
import seco.range # pylint: disable=import-error import seco.range # pylint: disable=import-error

View File

@ -25,6 +25,7 @@ integration.test: True
# Grains addons # Grains addons
grains: grains:
test_grain: cheese test_grain: cheese
grain_path: /tmp/salt-tests-tmpdir/file-grain-test
script: grail script: grail
alot: many alot: many
planets: planets:

View File

@ -77,6 +77,7 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin
self.assertIn('hello', ''.join(out)) self.assertIn('hello', ''.join(out))
self.assertIn('Succeeded: 1', ''.join(out)) self.assertIn('Succeeded: 1', ''.join(out))
@skipIf(True, 'This test causes the test to hang. Skipping until further investigation can occur.')
@destructiveTest @destructiveTest
@skip_if_not_root @skip_if_not_root
@skipIf(salt.utils.platform.is_windows(), 'This test does not apply on Windows') @skipIf(salt.utils.platform.is_windows(), 'This test does not apply on Windows')

View File

@ -357,7 +357,6 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
file. file.
''' '''
grain_path = os.path.join(TMP, 'file-grain-test') grain_path = os.path.join(TMP, 'file-grain-test')
self.run_function('grains.set', ['grain_path', grain_path])
state_file = 'file-grainget' state_file = 'file-grainget'
self.run_function('state.sls', [state_file]) self.run_function('state.sls', [state_file])

View File

@ -36,10 +36,7 @@ import salt.grains.core as core
# Import 3rd-party libs # Import 3rd-party libs
from salt.ext import six from salt.ext import six
if six.PY3: from salt._compat import ipaddress
import ipaddress
else:
import salt.ext.ipaddress as ipaddress
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -20,20 +20,11 @@ from tests.support.mock import (
) )
# Import Salt Libs # Import Salt Libs
from salt.ext import six
import salt.utils.network import salt.utils.network
import salt.utils.path import salt.utils.path
import salt.modules.network as network import salt.modules.network as network
from salt.exceptions import CommandExecutionError from salt.exceptions import CommandExecutionError
if six.PY2: from salt._compat import ipaddress
import salt.ext.ipaddress as ipaddress
HAS_IPADDRESS = True
else:
try:
import ipaddress
HAS_IPADDRESS = True
except ImportError:
HAS_IPADDRESS = False
@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(NO_MOCK, NO_MOCK_REASON)
@ -276,7 +267,7 @@ class NetworkTestCase(TestCase, LoaderModuleMockMixin):
self.assertDictEqual(network.connect('host', 'port'), self.assertDictEqual(network.connect('host', 'port'),
{'comment': ret, 'result': True}) {'comment': ret, 'result': True})
@skipIf(HAS_IPADDRESS is False, 'unable to import \'ipaddress\'') @skipIf(not bool(ipaddress), 'unable to import \'ipaddress\'')
def test_is_private(self): def test_is_private(self):
''' '''
Test for Check if the given IP address is a private address Test for Check if the given IP address is a private address
@ -288,7 +279,7 @@ class NetworkTestCase(TestCase, LoaderModuleMockMixin):
return_value=True): return_value=True):
self.assertTrue(network.is_private('::1')) self.assertTrue(network.is_private('::1'))
@skipIf(HAS_IPADDRESS is False, 'unable to import \'ipaddress\'') @skipIf(not bool(ipaddress), 'unable to import \'ipaddress\'')
def test_is_loopback(self): def test_is_loopback(self):
''' '''
Test for Check if the given IP address is a loopback address Test for Check if the given IP address is a loopback address

View File

@ -144,6 +144,36 @@ class DataTestCase(TestCase):
) )
) )
def test_subdict_match_with_wildcards(self):
'''
Tests subdict matching when wildcards are used in the expression
'''
data = {
'a': {
'b': {
'ç': 'd',
'é': ['eff', 'gee', '8ch'],
'ĩ': {'j': 'k'}
}
}
}
assert salt.utils.data.subdict_match(data, '*:*:*:*')
assert salt.utils.data.subdict_match(data, 'a:*:*:*')
assert salt.utils.data.subdict_match(data, 'a:b:*:*')
assert salt.utils.data.subdict_match(data, 'a:b:ç:*')
assert salt.utils.data.subdict_match(data, 'a:b:*:d')
assert salt.utils.data.subdict_match(data, 'a:*:ç:d')
assert salt.utils.data.subdict_match(data, '*:b:ç:d')
assert salt.utils.data.subdict_match(data, '*:*:ç:d')
assert salt.utils.data.subdict_match(data, '*:*:*:d')
assert salt.utils.data.subdict_match(data, 'a:*:*:d')
assert salt.utils.data.subdict_match(data, 'a:b:*:ef*')
assert salt.utils.data.subdict_match(data, 'a:b:*:g*')
assert salt.utils.data.subdict_match(data, 'a:b:*:j:*')
assert salt.utils.data.subdict_match(data, 'a:b:*:j:k')
assert salt.utils.data.subdict_match(data, 'a:b:*:*:k')
assert salt.utils.data.subdict_match(data, 'a:b:*:*:*')
def test_traverse_dict(self): def test_traverse_dict(self):
test_two_level_dict = {'foo': {'bar': 'baz'}} test_two_level_dict = {'foo': {'bar': 'baz'}}

View File

@ -984,6 +984,10 @@ class TestCustomExtensions(TestCase):
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, 'False') self.assertEqual(rendered, 'False')
rendered = render_jinja_tmpl("{{ 'fe80::20d:b9ff:fe01:ea8%eth0' | is_ipv6 }}",
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, 'True')
rendered = render_jinja_tmpl("{{ 'FE80::' | is_ipv6 }}", rendered = render_jinja_tmpl("{{ 'FE80::' | is_ipv6 }}",
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, 'True') self.assertEqual(rendered, 'True')
@ -996,6 +1000,10 @@ class TestCustomExtensions(TestCase):
''' '''
Test the `ipaddr` Jinja filter. Test the `ipaddr` Jinja filter.
''' '''
rendered = render_jinja_tmpl("{{ '::' | ipaddr }}",
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, '::')
rendered = render_jinja_tmpl("{{ '192.168.0.1' | ipaddr }}", rendered = render_jinja_tmpl("{{ '192.168.0.1' | ipaddr }}",
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, '192.168.0.1') self.assertEqual(rendered, '192.168.0.1')