一、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