一、Vue

1.关于图标的使用

项目里使用的是在iconfont下载的第三方图标,放在indata_tool_web\src\assets\iconfont目录下。在main.js引入

import './assets/iconfont/iconfont.css'

2.关于登录权限

原理:

利用vue-router的beforeEach方法,在进入路由之前,读取token,如果token不存在则跳转到登录页面。如果token存在,就带着token去认证,如果认证通过,即可访问,如果认证不通过,则跳转到登录页。

代码部分:

indata_dev\indata_tool_web\src\utils\permission.js

//权限认证
import Vue from 'vue'
import router from "../router";
import NProgress from 'nprogress' // Progress 进度条
import 'nprogress/nprogress.css'// Progress 进度条样式

let self = Vue.prototype
router.beforeEach((to, from, next) => {
  NProgress.start(); // 开启Progress
  if (to.path === "/login" || to.path === '/register') {
    self.DR.isLogin = true
    next()
  } else if (self.cookie.get('accessToken')) {
    Vue.http.get(self.api('validate_token')).then(
      (response) => {
        const res = JSON.parse(response.bodyText);
        if (res.state === 0) {
          self.cookie.set('user_name', res.user_name)
          next()
        } else {
          next('/login')
          self.DR.isLogin = true
        }
      },
      (response) => {
      }
    );

  } else {
    next('/login')
  }
  NProgress.done()
});

router.afterEach(() => {
  NProgress.done() // 结束Progress
});

引入:

在main.js里引入utils里的permission.js

//权限认证
import './utils/permission'

3.cookie封装

代码:

indata_dev\indata_tool_web\src\utils\vue-cookie.js

export default {
  install(Vue, options) {
    Vue.prototype.cookie = {
      set: function (name, value, days) {
        let d = new Date;
        d.setTime(d.getTime() + 24 * 60 * 60 * 1000 * days);
        window.document.cookie = name + "=" + value + ";path=/;expires=" + d;
      },
      get: function (name) {
        let v = window.document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
        return v ? v[2] : null;
      },
      delete: function (name) {
        this.set(name, '', -1);
      }
    };
  }
}

引用:

在main.js里引入

import VueCookie from './utils/vue-cookie'
Vue.use(VueCookie)

使用:

//存
this.cookie.set('key', 'value');
//读
this.cookie.get('key');

4.localStorage封装

代码部分:

D:\Repository\Django\light_restructure\indata_dev\indata_tool_web\src\utils\vue-localStorage.js

export default {
  install(Vue, options) {
    /**
     * 从localStorage中存/取数据
     * @type {{set(*=, *=): void, get(*=): *}}
     */
    Vue.prototype.LS = {
      /**
       * 存数据
       * @param key 键: 字符串
       * @param value 值:数组、对象、字符串
       */
      set(key, value) {
        value = typeof(value) === "object" ? JSON.stringify(value) : value;
        window.localStorage.setItem(key, value);
      },
      /**
       * 读数据
       * @param key
       * @returns {*}
       */
      get(key) {
        try {
          return JSON.parse(window.localStorage.getItem(key));
        } catch (e) {
          return window.localStorage.getItem(key)
        }
      }
    }
  }
}

5.Vue-resource拦截器的使用

解决ie浏览器缓存问题

在项目里,利用vue-resource拦截器,来解决禁止ie浏览器缓存的的问题。

思路:拦截所有请求,并在请求后添加时间戳,从而使每次发送的请求都不一样,解决由于缓存带来的问题。

在main.js里添加:

Vue.http.interceptors.push((request, next) => {
  request.url += (request.url.indexOf('?') > 0 ? '&' : '?') + new Date().getTime()
  next((response) => {
    return response;
  });
});

利用interceptors设置请求方法

  // modify method
  request.method = 'POST';

利用interceptors设置请求头

  // modify headers
  request.headers.set('X-CSRF-TOKEN', 'TOKEN');
  request.headers.set('Authorization', 'Bearer TOKEN');

6.统一URL封装,解决前后端分离开发,域名改动问题

利用全局变量,对URL进行封装,将所有请求用到的url都放到一起,然后根据是否跨域来返回url。而且这样做还有一个好处就是,url统一管理,当需要变更请求的时候,不需要去修改项目里的代码,只需改这个位置的代码即可。

代码部分:

indata_dev\indata_tool_web\src\data\DataRepository.js

export default {
  install(Vue, options) {
    Vue.prototype.api = function (name) {
      //是否跨域
      let crossDomain = false;
      let http = {
        host: 'http://localhost',
        port: '8000'
      };
      let urls = {
        login: '/api/login',
        register: '/api/register',
        validate_config_name: '/api/validate/config_name',
        validate_token: 'api/validate/token',
        edit_config_name: 'api/config/name/edit',
        save_config: '/api/config/save',
        get_config: '/api/config/get',
        get_config_complete: '/api/config/complete',
        del_config: '/api/config/del',
        all_config: '/api/config/all',
        get_config_by: '/api/config/by/get',
        get_data_all: '/api/data/all',
        save_base: '/api/base/save',
        get_base: '/api/base/get',
        save_node: '/api/node/save',
        get_node: '/api/node/get',
        save_service: '/api/service/save',
        get_service: '/api/service/get',
        save_component: '/api/component/save',
        get_component: '/api/component/get',
        save_disk: '/api/disk/save',
        get_disk: '/api/disk/get',
        config_down: '/api/config/down',
        config_deploy: '/api/config/deploy',
        task_get_by: '/api/task/by/get',
        task_update: '/api/task/update',
        jenkins_stop_job: 'api/jenkins/stop/job',
        jenkins_start_job: 'api/jenkins/start/job'
      };
      return crossDomain ? http.host + ':' + http.port + urls[name] : urls[name]
    };
    Vue.prototype.jenkins = {
      jenkins_redirect: '/api/jenkins/redirect',
      pipelines_context: '/blue/rest/organizations/jenkins/pipelines/',
      redirect(url) {
        const jenkins_redirect = '/api/jenkins/redirect';
        return jenkins_redirect + '?url=' + encodeURIComponent(url)
      }
    };
  }
}  

引入:

在main.js里引入

import DataRepository from './data/DataRepository.js'
Vue.use(DataRepository)

使用:

比如,我要获取登录的api

logonApi = this.DR.api('logon')

7.vue组件间的通讯

a 父组件向子组件传值

思路:父组件利用v-bind(简写:)来绑定属性,子组件通过props来接受父组件数据。并且子组件可以通过watch监听props属性,来赋值给本组件的data,在js里通过this来使用数据。

注意,此时数据不是双向绑定的。

父组件:

sendToChild为父组件要传给子组件的数据,acceptFromParent是子组件props里要接受的数据

<template>
  <div>
    <my-child :acceptFromParent="sendToChild"></my-child>
    <input v-model="sendToChild"/>
  </div>
</template>

<script>
  import MyChild from "./Childrens/MyChild";
  export default {
    components: {MyChild},
    name: 'HelloWorld',
    data() {
      return {
        sendToChild:''
      }
    }
  }
</script>
子组件:

通过acceptFromParent来接受父组件数据,并且利用watch来监听数据,并赋值给data里的inputData

<template>
  <div>
    <h1>Hello</h1>
    <h3>来自父组件:{{acceptFromParent}}</h3>
    <p>本组件Data:{{inputData}}</p>
  </div>
</template>

<script>
  export default {
    name: "my-child",
    props: ['acceptFromParent'],
    data() {
      return {
        inputData: ''
      }
    },
    watch: {
      acceptFromParent(curVal, oldVal) {
        this.inputData = curVal
      }
    }
  }
</script>

b 子组件向父组件传值

思路:子组件向父组件传值,子组件利用this.$emit(event,…args)来传值,父组件通过监听event然后指定方法接受传递的值。

子组件:
<template>
  <div>
    <input v-model="inputData" @keyup="sendData">
  </div>
</template>

<script>
    methods: {
      sendData() {
        this.$emit('sendToParent', this.inputData)
      }
    },
</script>
父组件:
<template>
  <div>
    <my-child :acceptFromParent="sendToChild" @sendToParent="acceptFromChild"></my-child>
  </div>
</template>
<script>
  methods:{
        acceptFromChild(data){
          console.log(data)
        }
  }
</script>

c 跨组件、同级组件传值

利用eventBus在同级组件间通讯。

eventBus.js
import Vue from 'vue'
export default new Vue()
组件一(发送):
import {EventBus} from "../../utils/eventBus.js";
eventBus.$emit('functionName','sendData')
组件二(接受):
import {EventBus} from "../../utils/eventBus.js";
mounted(){
  eventBus.$on('functionName',(val)=>{
    console.log(val)
  })
}

d 父组件调用子组件方法

通过给子组件的ref一个属性,然后通过调用this.$ref.属性.方法 就可以调用子组件的方法了。

父组件:
<my-child ref ="myChild" :acceptFromParent="sendToChild" @sendToParent="acceptFromChild"></my-child>

mounted(){
    this.$refs.myChild.alertData("please alert data")
}
子组件:
methods:{
  alertData(val){
    alert(val)
  }
}

e 子组件调用父组件方法

思路与传值一样

二、JavaScript

js判断数组是否为空
let array = []

if(array[0]){
  //数组不为空
}

if(array.length>0){
  //数组不为空
}
js数组添加元素
let array = []
array.push('……')
js数组删除指定元素
let array= []
array.splice(i,1) //删除i位置的元素
js数组在指定位置添加元素
let array = []
array.splice(i,0,'要添加的内容')
js取数组指定范围元素
let array=[]
array.slice(start,end)
三目运算符的使用
addData.root_pwd = root_pwd ? root_pwd : '';

三、Django

1.使方法可以接受不带csrf的POST请求

在方法前添加@csrf_exempt修饰符,如:

@csrf_exempt
def validate_config_name(request):
    response = {}
    user_name = request.COOKIES['user_name']
    config_name = request.POST.get('config_name')
    try:
        ConfigInfo.objects.get(config_name=config_name, user_name=user_name)
        response['msg'] = 'already'
        response['state'] = 1
    except Exception as e:
        response['msg'] = str(e)
        response['state'] = 0
        response['data'] = generate_config_id()
    return JsonResponse(response)

2.在Django中使用日志

简单配置,配置日志在控制台输出:

在settings.py里配置日志

import logging
import django.utils.log
import logging.handlers

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
        },
    },
}

使用:

import logging
logger = logging.getLogger("django")

logger.info(msg)

详细配置:

http://blog.51cto.com/davidbj/1433741

3.Django中的update_or_create方法

这个例子中,根据config_info_id 来判断,如果存在数据,就更新,更新的是set_data里的数据。如果没有,就创建。

 set_data = {
   'config_info_id':''……'
 }
 ConfigInfo.objects.update_or_create(config_info_id=set_data['config_info_id'], defaults=set_data)

4.格式化数据库读取时间

tmp_obj[key] = info[key].strftime("%Y年%m月%d日 %H:%M:%S")

5.django下载文件

    # 下载
    dow_path = "tmp/test.zip"
    f = open(down_path, 'rb')
    response = FileResponse(f)
    response['Content-Type'] = 'application/octet-stream'
    response['Content-Disposition'] = 'attachment;filename=' + server.config_name_no_time + '.zip'
    return response

6.将django查询的object转成json字典

如:使用objects.get()方法查询的数据,可以用一下方法转成json

def object_to_json(obj):
    return dict([(kk, obj.__dict__[kk]) for kk in obj.__dict__.keys() if kk != "_state"])

四、Python

1.遍历字典(或者json)的key,value

data = {
    'key1': 'value1',
    'key2': 'value2',
    'key3': 'value3'
}
for key, value in data.items():
    print key, value

2.利用enumerate遍历列表的下索引和值

data_array = [
    'value1', 'value2', 'value3'
]
for key, value in enumerate(data_array):
    print key, value

3.索引从指定范围循环

如索引从6到9循环

for index in range(6, 9):
    print index

4.list()将元组转化成列表

什么是元组?如下用“()”括起来的并且以“,”分隔的就是算组。元组的遍历与列表相似。如果元组里只有一个元素时,后面要加“,”如tup1=(1,)

# 元组
tup1 = (1, u"二", 'three')

list的可以将元组转化成列表

    list1 = list(tup1)
    print list1

结果:

[1, u'\u4e8c', 'three']

5.利用urllib,urllib2发送http请求

封装方法

def request_url(url, data=None, header=None):
    req = urllib2.Request(url, data=urllib.urlencode(data))
    if header is not None:
        for header_name, header_value in header.items():
            req.add_header(header_name, header_value)
    response = {}
    try:
        res = urllib2.urlopen(req)
        response['state'] = 0
        response['data'] = res.read()
    except urllib2.HTTPError, e:
        response['state'] = 1
        response['msg'] = {
            'code': e.code,
            'reason': e.reason,
            'url': e.geturl(),
            'info': e.read()
        }
    return response

测试:

    url = 'http://127.0.0.1:8000/api/config/all?page=1&pagesize=5&1517736445175'
    res = request_url(url)
    print(res)

结果:

是失败的,经过我们查看错误信息,是因为我们在请求头里面,没有添加cookie。

添加cookie:

    url = 'http://127.0.0.1:8000/api/config/all?page=1&pagesize=5&1517736445175'
    cookie = 'test_cookie="set cookie"; accessToken=e64781ffc59bf974656dadd760857fbe1fb8d0acMTUxNzczMjkyODo2MDQ4MDA; user_name=shirukai'
    header = {
        'Cookie': cookie
    }
    res = request_url(url, header=header)
    print(res)

6.python 对目录文件的操作

判断文件夹是否存在,存在删除、不存在创建

        config_path = "tmp/test"
        is_exist = os.path.exists(config_path)
        if is_exist:
            shutil.rmtree(config_path)
        os.makedirs(config_path)

os.makedirs(path,mode=0o777) 是递归创建目录

os.mkdir()创建目录

创建json文件,并写入数据

        file_path = 'tmp/test.json'
        data={
          'key1','value1'
        }
        with open(file_path, 'w') as f:
            f.write(json.dumps(data, indent=4))

利用shutil.copyfile复制文件

import shutil
old_path = 'tmp/test1.json'
new_path = 'tmp/test2.json'
shutil.copyfile(old_path, new_path)

zip打包文件

    def pack_zip(config_path):
        f = zipfile.ZipFile(config_path + '.zip', 'w', zipfile.ZIP_DEFLATED)
        for dir_path, dir_names, file_names in os.walk(config_path):
            for filename in file_names:
                f.write(os.path.join(dir_path, filename), os.path.join(dir_path, filename).replace(config_path, ''))
        f.close()
        return config_path + '.zip'

7.python字符串操作

方法 描述
string.capitalize() 把字符串的第一个字符大写
string.center(width) 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串
string.count(str, beg=0, end=len(string)) 返回 str 在 string 里面出现的次数,如果 beg 或者 end 指定则返回指定范围内 str 出现的次数
string.decode(encoding=’UTF-8’, errors=’strict’) 以 encoding 指定的编码格式解码 string,如果出错默认报一个 ValueError 的 异 常 , 除非 errors 指 定 的 是 ‘ignore’ 或 者’replace’
string.encode(encoding=’UTF-8’, errors=’strict’) 以 encoding 指定的编码格式编码 string,如果出错默认报一个ValueError 的异常,除非 errors 指定的是’ignore’或者’replace’
string.endswith(obj, beg=0, end=len(string)) 检查字符串是否以 obj 结束,如果beg 或者 end 指定则检查指定的范围内是否以 obj 结束,如果是,返回 True,否则返回 False.
string.expandtabs(tabsize=8) 把字符串 string 中的 tab 符号转为空格,tab 符号默认的空格数是 8。
string.find(str, beg=0, end=len(string)) 检测 str 是否包含在 string 中,如果 beg 和 end 指定范围,则检查是否包含在指定范围内,如果是返回开始的索引值,否则返回-1
string.format() 格式化字符串
string.index(str, beg=0, end=len(string)) 跟find()方法一样,只不过如果str不在 string中会报一个异常.
string.isalnum() 如果 string 至少有一个字符并且所有字符都是字母或数字则返回 True,否则返回 False
string.isalpha() 如果 string 至少有一个字符并且所有字符都是字母则返回 True,否则返回 False
string.isdecimal() 如果 string 只包含十进制数字则返回 True 否则返回 False.
string.isdigit() 如果 string 只包含数字则返回 True 否则返回 False.
string.islower() 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 False
string.isnumeric() 如果 string 中只包含数字字符,则返回 True,否则返回 False
string.isspace() 如果 string 中只包含空格,则返回 True,否则返回 False.
string.istitle() 如果 string 是标题化的(见 title())则返回 True,否则返回 False
string.isupper() 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True,否则返回 False
string.join(seq) 以 string 作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串
string.ljust(width) 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串
string.lower() 转换 string 中所有大写字符为小写.
string.lstrip() 截掉 string 左边的空格
string.maketrans(intab, outtab]) maketrans() 方法用于创建字符映射的转换表,对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标。
max(str) 返回字符串 str 中最大的字母。
min(str) 返回字符串 str 中最小的字母。
string.partition(str) 有点像 find()和 split()的结合体,从 str 出现的第一个位置起,把 字 符 串 string 分 成 一 个 3 元 素 的 元 组 (string_pre_str,str,string_post_str),如果 string 中不包含str 则 string_pre_str == string.
string.replace(str1, str2, num=string.count(str1)) 把 string 中的 str1 替换成 str2,如果 num 指定,则替换不超过 num 次.
string.rfind(str, beg=0,end=len(string) ) 类似于 find()函数,不过是从右边开始查找.
string.rindex( str, beg=0,end=len(string)) 类似于 index(),不过是从右边开始.
string.rjust(width) 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串
string.rpartition(str) 类似于 partition()函数,不过是从右边开始查找.
string.rstrip() 删除 string 字符串末尾的空格.
string.split(str=””, num=string.count(str)) 以 str 为分隔符切片 string,如果 num有指定值,则仅分隔 num 个子字符串
[string.splitlines(keepends]) 按照行(‘\r’, ‘\r\n’, \n’)分隔,返回一个包含各行作为元素的列表,如果参数 keepends 为 False,不包含换行符,如果为 True,则保留换行符。
string.startswith(obj, beg=0,end=len(string)) 检查字符串是否是以 obj 开头,是则返回 True,否则返回 False。如果beg 和 end 指定值,则在指定范围内检查.
string.strip([obj]) 在 string 上执行 lstrip()和 rstrip()
string.swapcase() 翻转 string 中的大小写
string.title() 返回”标题化”的 string,就是说所有单词都是以大写开始,其余字母均为小写(见 istitle())
string.translate(str, del=””) 根据 str 给出的表(包含 256 个字符)转换 string 的字符,要过滤掉的字符放到 del 参数中
string.upper() 转换 string 中的小写字母为大写
string.zfill(width) 返回长度为 width 的字符串,原字符串 string 右对齐,前面填充0
string.isdecimal() isdecimal()方法检查字符串是否只包含十进制字符。这种方法只存在于unicode对象。

一次替换多个变量

第一个参数text:需要替换的文本

第二个参数replace_dict:需要替换的字典集

def multiple_replace(text, replace_dict):
    for key, value in replace_dict.items():
        text = text.replace(key, value)
    return text

8.python调用shell脚本

项目里使用:

  import commands
  (state, output) = commands.getstatusoutput('sh ' + config_path + '/copy_config.sh')

https://www.cnblogs.com/yangykaifa/p/7127776.html

9.合并字典

用dict(a.items()+b.items())方法合并字典

if __name__ == '__main__':
    a = {'key1': 'value1', 'key2': 'value2'}
    b = {'key3': 'value3', 'key4': 'value4'}
    c = dict(a.items() + b.items())
    print(c)
/usr/bin/python2.7 /Repository/python/testapi/TestDict.py
{'key3': 'value3', 'key2': 'value2', 'key1': 'value1', 'key4': 'value4'}

Process finished with exit code 0

用dict(a,**b)合并字典

if __name__ == '__main__':
    a = {'key1': 'value1', 'key2': 'value2'}
    b = {'key3': 'value3', 'key4': 'value4'}
    d = dict(a, **b)
    print(d)
/usr/bin/python2.7 /Repository/python/testapi/TestDict.py
{'key3': 'value3', 'key2': 'value2', 'key1': 'value1', 'key4': 'value4'}

Process finished with exit code 0

用c.update(a) c.update(b)合并

if __name__ == '__main__':
    a = {'key1': 'value1', 'key2': 'value2'}
    b = {'key3': 'value3', 'key4': 'value4'}
    d = {}
    d.update(a)
    d.update(b)
    print(d)
/usr/bin/python2.7 /Repository/python/testapi/TestDict.py
{'key3': 'value3', 'key2': 'value2', 'key1': 'value1', 'key4': 'value4'}

Process finished with exit code 0

10.合并数组

a+b

>>> a =[1,2,3,4,5,6]
>>> b =['a','b','c']
>>> a+b
[1, 2, 3, 4, 5, 6, 'a', 'b', 'c']
>>> 

a+=b

>>> a+=b
>>> print a
[1, 2, 3, 4, 5, 6, 'a', 'b', 'c']

a.extend(b)

>>> a.extend(b)
>>> print a
[1, 2, 3, 4, 5, 6, 'a', 'b', 'c', 'a', 'b', 'c']

a.append(b)

>>> print a
[1, 2, 3, 4, 5, 6, 'a', 'b', 'c', 'a', 'b', 'c', ['a', 'b', 'c']]

a[0:0] =b

>>> a[0:0] = b
>>> print a
['a', 'b', 'c', 1, 2, 3, 4, 5, 6, 'a', 'b', 'c', 'a', 'b', 'c', ['a', 'b', 'c']]
>>> 

五、Jenkins

在项目中调用Jenkins的api,并对jenkins进行自定义的api封装

jenkisn api 官网:https://python-jenkins.readthedocs.io/en/latest/api.html

1.安装python-jenkins

pip install python-jenkins

2.关于jenkins请求权限认证的问题

python-jenkins提供的api有时候,满足不了我们的个性化需求,所以可以从jenkins的页面上抓取连接,在python里请求数据,并进一步处理。但是这个时候,往往是需要jenkins权限认证的。

解决jenkins的权限认证,主要在请求头里添加认证信息即可。

jenkins的认证信息:

jenkins的认证信息其实就是Base64编码后的用户名和密码。可以用一下的方法,得到编码后的认证信息

import six
import base64

def auth_headers(username, password):
    auth = '%s:%s' % (username, password)
    if isinstance(auth, six.text_type):
        auth = auth.encode('utf-8')
    return b'Basic ' + base64.b64encode(auth)
说明

包:six 是兼容包,base64是编解码包

方法参数:username是登录jenkins的用户名,password是密码

返回值:就是一个编码后的字符串。

将认证信息添加到请求头里 :

add_header('Authorization','生成的Auth')

3.利用请求转发解决跨域问题

这里使用的django后台请求转发来解决的跨域问题。

思路:django统一提供一个api,前台将要转发的url进行urlencode编码后作为参数,发送到这个统一的api,

后台得到参数后,解码,根据jenkins的host、port组合信息的url。然后利用urllib发送http请求,得到数据之后,返回给django,django再返回给前端。

后台提供请求转发api:

urls.py

在urls.py里添加url

    url(r'jenkins/redirect', views.jenkins_url_redirect),
views.py

在views.py里添加方法:

@csrf_exempt
def jenkins_url_redirect(request):
    service = jenkins_api.JenkinsApi()
    if request.method == 'POST':
        url = request.GET.get('url')
        res_data = json.loads(request.POST.get('data'))
        req = service.jenkins_url_redirect(url, res_data)
    else:
        url = request.GET.get('url')
        req = service.jenkins_url_redirect(url)
    return JsonResponse(req, safe=False)

请求转发jenkins_url_redirect(封装的jenkins_api里的一个方法):

'''
jenkins api 重定向 
解决jenkins跨域、权限问题
encode url
'''

def jenkins_url_redirect(self, url, res_data=None):
    url = urllib.quote(url)
    if res_data is not None:
        req = self.request_url(url, res_data)
    else:
        req = self.request_url(url)
    if req['state'] == 0:
        if req['content_type'].find('json') == -1:
            return req
        else:
            return json.loads(req['data'])
    else:
        return req['msg']

例子:

前台请求

http://127.0.0.1:8000/api/jenkins/redirect?url=%2Fblue%2Frest%2Forganizations%2Fjenkins%2Fpipelines%2Findata_config1516955057.63%2Fruns%2F&1517797001138

4.抓取jenkins pipline里的api做日志显示

工具里的日志显示,主要是去抓取pipline里的api来得到数据的。

大体流程:得到job_name–>获取所有buid–>获取所有nodes信息–>获取node详细信息–>查看当前node日志

以 job_name=indata_config1516955057.63为例子(job_name 为jenkins的job名):

第一步:获取所有buids

api:

/blue/rest/organizations/jenkins/pipelines/job_name/runs

例如

/blue/rest/organizations/jenkins/pipelines/indata_config1516955057.63/runs
返回值:

返回值为所有buids的数组,里面包含buids的信息。

假设返回的数组为:buidsArray

第二步:获取当前buid里的所有nodes信息

api:

可以从上一步获得buidsArray里获取nodes的api,获取方式如下

nodesUrl = buidsArray[0]['_links']['nodes']['href']

如:/blue/rest/organizations/jenkins/pipelines/indata_config1516955057.63/runs/1/nodes/

返回值:

返回值为所有nodes信息,并且包含nodes的信息

假设返回的数组信息为:nodesArray

第三步:获取node详细信息

api:

可以从上一步得到的nodesArray里获取

node_url = nodesArray[0]['_links']['steps']['href']

如:/blue/rest/organizations/jenkins/pipelines/indata_config1516955057.63/runs/1/nodes/6/steps/

返回值:

返回值为当前节点的详细信息

假设返回信息为:nodeInfo

第四步:获取日志

api:

可以从上一步的返回信息里得到api

log_url = nodeInfo[0]['actions']['_links']['self']['href']

如:/blue/rest/organizations/jenkins/pipelines/indata_config1516955057.63/runs/1/nodes/31/steps/32/log/

返回值:

5.封装jenkins_api

# -*- coding: utf-8 -*-
import urllib
import urllib2
import json
import logging
import six
import base64
import jenkins
from params import JENKINS, COPY_PATH
import commands
import os
from utils import multiple_replace

# jenkins api
# 创建 credentials
CREATE_CREDENTIAL = '/credentials/store/system/domain/_/createCredentials'
# 获取 credentials
GET_CREDENTIAL = '/credentials/store/system/domain/_/api/json?depth=1'
# 删除 credentials $id:c13d4b89-e7b0-4e68-bb76-bac36afe7f92
DELETE_CREDENTIAL = '/credentials/store/system/domain/_/credential/$id/doDelete'
# 获取当前登录用户的credentials
CRUMB_URL = '/crumbIssuer/api/json'


def auth_headers(username, password):
    auth = '%s:%s' % (username, password)
    if isinstance(auth, six.text_type):
        auth = auth.encode('utf-8')
    return b'Basic ' + base64.b64encode(auth)


class JenkinsApi(object):
    def __init__(self):
        self.add_crumb = False
        self.url = 'http://' + JENKINS['host'] + ':' + JENKINS['port']
        self.auth = auth_headers(JENKINS['username'], JENKINS['password'])
        self.crumb = self.__add_crumb()
        self.service = jenkins.Jenkins('http://' + JENKINS['host'] + ':' + JENKINS['port'],
                                       username=JENKINS['username'],
                                       password=JENKINS['password'])

    '''
    添加crumb
    '''

    def __add_crumb(self):
        res = self.request_url(CRUMB_URL, None)
        if res['state'] == 0:
            self.add_crumb = True
            return json.loads(res['data'])
        else:
            return None

    '''
    创建 credential
    :param
        description:描述
        username:用户名
        password:密码
    :return 
        credential id
    '''

    def create_credential(self, description, username, password):
        req_data = {
            "json": {
                "": "0",
                "credentials": {
                    "scope": "GLOBAL",
                    "id": "",
                    "username": str(username),
                    "password": str(password),
                    "description": str(description),
                    "stapler-class": "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"
                }
            }
        }
        # 创建
        create_res = self.request_url(CREATE_CREDENTIAL, req_data)
        # # 获取
        if create_res['state'] == 0:
            get_res = self.request_url(GET_CREDENTIAL)
            if create_res['state'] == 0:
                json_res = json.loads(get_res['data'])['credentials']
                for i in json_res:
                    if i['description'] == description:
                        return i

    '''
    删除 credential
    :param
        credential_id
    :return
        response    
    '''

    def delete_credential(self, credential_id):
        api = DELETE_CREDENTIAL.replace('$id', credential_id)
        req = self.request_url(api)
        return req

    '''
    jenkins api 重定向 
    解决jenkins跨域、权限问题
    encode url
    '''

    def jenkins_url_redirect(self, url, res_data=None):
        url = urllib.quote(url)
        if res_data is not None:
            req = self.request_url(url, res_data)
        else:
            req = self.request_url(url)
        if req['state'] == 0:
            if req['content_type'].find('json') == -1:
                # if url[-4:-1] == 'log' or url[-9:-1] == 'doDelete':
                return req
            else:
                return json.loads(req['data'])
        else:
            return req['msg']

    def get_log(self, url):
        return self.request_url(url)

    def stop_job(self, job_name):
        # 获取job信息
        job_info = self.service.get_job_info(job_name)
        job_number = job_info['lastBuild']['number']
        self.service.stop_build(job_name, job_number)
        return json.dumps(job_number)

    def start_job(self, job_name):
        self.service.build_job(job_name)

    @staticmethod
    def copy_file(config_path):
        (state, output) = commands.getstatusoutput('sh ' + config_path + '/copy_config.sh')
        print(output.decode("GBK"))
        return state, output.decode("GBK")

    def create_node(self, node_name, config_name, username, password, ip):
        credential_ids = self.create_credential(config_name, username, password)
        credential_id = credential_ids['id']
        params = {
            'port': '22',
            'username': username,
            'credentialsId': credential_id,
            'host': ip
        }
        # 检查node是否存在
        try:
            self.service.get_node_info(node_name)
        except Exception as e:
            print(str(e))
            self.service.create_node(
                node_name,
                nodeDescription=node_name,
                remoteFS='/opt/jenkins',
                labels=node_name,
                exclusive=True,
                launcher=jenkins.LAUNCHER_SSH,
                launcher_params=params
            )
            # self.server.enable_node(self.node_name)
        return credential_id

    def create_job(self, node_name, config_name, ip, xml_path=os.path.join(COPY_PATH, 'config.xml')):
        file_path = xml_path
        f = open(file_path)
        config_context = f.read()
        f.close()
        replace_dict = dict()
        replace_dict['$node_name'] = node_name
        replace_dict['$ambari-server-ip'] = ip
        config = multiple_replace(str(config_context), replace_dict)
        self.service.create_job(config_name, config)
        self.service.build_job(config_name)

    def request_url(self, api, data=None):
        if data is not None:
            req = urllib2.Request(url=self.url + api, data=urllib.urlencode(data))
        else:
            req = urllib2.Request(url=self.url + api)
        if self.auth is not None:
            req.add_header('Authorization', self.auth)
        if self.add_crumb:
            req.add_header(self.crumb['crumbRequestField'], self.crumb['crumb'])
        req.add_header('Accept',
                       'application/json, text/plain, */*')
        response = {}
        try:
            res = urllib2.urlopen(req)
            response['state'] = 0
            response['data'] = res.read()
            response['content_type'] = res.info()['Content-Type']
        except urllib2.HTTPError, e:
            response['state'] = 1
            response['msg'] = {
                'code': e.code,
                'reason': e.reason,
                'url': e.geturl(),
                'info': e.read()
            }
        return response
0%