v0.4.0 k8s 基本功能

This commit is contained in:
何全
2019-06-12 11:40:16 +08:00
parent de5fd03147
commit 34de510eff
11 changed files with 484 additions and 51 deletions

View File

@@ -34,7 +34,7 @@
* 一期: 基础模板 (已完成)
* 二期: k8s管理平台 (开发中)
* pod 列表 (已完成)
* node/service/pod 列表 (已完成)
* pod webssh (已完成)

View File

@@ -28,7 +28,7 @@ class MyThread(threading.Thread):
self.number += 1
if "kubectl exec -it" in str_data:
#不返回内容
# 不返回内容
pass
else:
if "rpc error" in str_data:
@@ -68,6 +68,7 @@ class EchoConsumer(WebsocketConsumer):
def connect(self):
# 创建channels group 命名为:用户名 (最好不要中文名字)并使用channel_layer写入到redis
async_to_sync(self.channel_layer.group_add)(self.scope['user'].username, self.channel_name)
self.sshclient = paramiko.SSHClient()
@@ -80,6 +81,7 @@ class EchoConsumer(WebsocketConsumer):
t1 = MyThread(self)
t1.setDaemon(True)
t1.start()
# 可以在这里根据 用户 要访问的pod 进行 权限控制
path = self.scope['path'].split('/')
cmd = f"kubectl exec -it {path[2]} -n {path[3]} sh \r"
self.chan.send(cmd)

View File

@@ -20,35 +20,50 @@ class K8sApi(object):
v1 = client.CoreV1Api(aApiClient)
return v1
def get_podlist(self):
def get_node_list(self):
client_v1 = self.get_client()
ret = client_v1.list_node()
return ret
def get_service_list(self):
client_v1 = self.get_client()
ret = client_v1.list_service_for_all_namespaces(watch=False)
return ret
def get_pod_list(self):
client_v1 = self.get_client()
ret_pod = client_v1.list_pod_for_all_namespaces(watch=False)
return ret_pod
def get_namespacelist(self):
def get_pod_detail(self, name, namespace):
client_v1 = self.get_client()
ret_pod = client_v1.read_namespaced_pod(name, namespace)
return ret_pod
def get_namespace_list(self):
client_v1 = self.get_client()
ret_namespace = client_v1.list_namespace()
return ret_namespace
def test_pods_connect(self, podname, namespace, command, container=None):
client_v1 = self.get_client()
if stream(client_v1.connect_get_namespaced_pod_exec, podname, namespace, command=command,
container=container,
stderr=True, stdin=False,
stdout=True, tty=False):
return True
else:
return False
def get_pods_exec(self, podname, namespace, command, container=None):
client_v1 = self.get_client()
if container:
rest = stream(client_v1.connect_get_namespaced_pod_exec, podname, namespace, command=command,
container=container,
stderr=True, stdin=False,
stdout=True, tty=False)
else:
rest = stream(client_v1.connect_get_namespaced_pod_exec, podname, namespace, command=command,
stderr=True, stdin=False,
stdout=True, tty=False)
return rest
# def test_pod_connect(self, podname, namespace, command, container=None):
# client_v1 = self.get_client()
# if stream(client_v1.connect_get_namespaced_pod_exec, podname, namespace, command=command,
# container=container,
# stderr=True, stdin=False,
# stdout=True, tty=False):
# return True
# else:
# return False
#
# def get_pod_exec(self, podname, namespace, command, container=None):
# client_v1 = self.get_client()
# if container:
# rest = stream(client_v1.connect_get_namespaced_pod_exec, podname, namespace, command=command,
# container=container,
# stderr=True, stdin=False,
# stdout=True, tty=False)
# else:
# rest = stream(client_v1.connect_get_namespaced_pod_exec, podname, namespace, command=command,
# stderr=True, stdin=False,
# stdout=True, tty=False)
# return rest

View File

@@ -12,9 +12,12 @@ def main():
client.Configuration.set_default(configuration)
v1 = client.CoreV1Api()
ret = v1.list_pod_for_all_namespaces(watch=False)
for i in ret.items:
print("%s\t%s\t%s" %
(i.status.pod_ip, i.metadata.namespace, i.metadata.name))
ret1 = v1.read_namespaced_pod("nginx-58bdcbcd-z8v2s","default")
print(ret1)
# for i in ret.items:
# print("%s\t%s\t%s" %
# (i.status.pod_ip, i.metadata.namespace, i.metadata.name))
if __name__ == '__main__':

View File

@@ -1,8 +1,14 @@
from django.urls import path
from k8s import views
app_name = "k8s"
urlpatterns = [
path('k8s-node-list', views.K8sNodeListView.as_view(), name='k8s-node-list'),
path('k8s-service-list', views.K8sServiceListView.as_view(), name='k8s-service-list'),
path('k8s-pod-list', views.K8sPodListView.as_view(), name='k8s-pod-list'),
path('k8s-pod-webssh', views.K8sPodWebssh.as_view(), name='k8s-pod-webssh')
path('k8s-pod-webssh', views.K8sPodWebssh.as_view(), name='k8s-pod-webssh'),
path('k8s-pod-detail', views.K8sPodDetail.as_view(), name='k8s-pod-detail')
]

View File

@@ -16,12 +16,48 @@ from k8s.k8sApi.core import K8sApi
logger = logging.getLogger('k8s')
class K8sNodeListView(LoginRequiredMixin, PermissionRequiredMixin, View):
permission_required = ('k8s.view_ecs',)
def get(self, request):
obj = K8sApi()
ret = obj.get_node_list()
data = {}
for i in ret.items:
print(i)
data[i.metadata.name] = {"name": i.metadata.name,
"status": i.status.conditions[-1].type if i.status.conditions[ -1].status == "True" else "NotReady",
"ip": i.status.addresses[0].address,
"kubelet_version": i.status.node_info.kubelet_version,
"os_image": i.status.node_info.os_image,
}
return render(request, "k8s/k8s-node-list.html", {"data": data})
class K8sServiceListView(LoginRequiredMixin, PermissionRequiredMixin, View):
permission_required = ('k8s.view_ecs',)
def get(self, request):
obj = K8sApi()
ret = obj.get_service_list()
data = {}
for i in ret.items:
print(i)
ports = []
for j in i.spec.ports:
ports.append(f"{j.target_port}/{j.port}/{j.node_port}")
data[i.metadata.name] = {"name": i.metadata.name, "cluster_ip": i.spec.cluster_ip, "type": i.spec.type,
"external_i_ps": i.spec.external_i_ps,
"port": ports}
return render(request, "k8s/k8s-service-list.html", {"data": data})
class K8sPodListView(LoginRequiredMixin, PermissionRequiredMixin, View):
permission_required = ('k8s.view_ecs',)
def get(self, request):
obj = K8sApi()
ret = obj.get_podlist()
ret = obj.get_pod_list()
data = {}
for i in ret.items:
data[i.metadata.name] = {"ip": i.status.pod_ip, "namespace": i.metadata.namespace}
@@ -34,5 +70,15 @@ class K8sPodWebssh(LoginRequiredMixin, PermissionRequiredMixin, View):
def get(self, request):
name = self.request.GET.get("name")
namespace = self.request.GET.get("namespace")
return render(request, "k8s/k8s-pod-webssh.html",{"name":name,"namespace":namespace})
return render(request, "k8s/k8s-pod-webssh.html", {"name": name, "namespace": namespace})
class K8sPodDetail(LoginRequiredMixin, PermissionRequiredMixin, View):
permission_required = ('k8s.view_ecs',)
def get(self, request):
name = self.request.GET.get("name")
namespace = self.request.GET.get("namespace")
obj = K8sApi()
data = obj.get_pod_detail(name, namespace)
return render(request, "k8s/k8s-pod-detail.html", {"name": name, "namespace": namespace, "data": data})

View File

@@ -55,19 +55,28 @@
</ul>
</li>
{% if perms.k8s.view_ecs %}
<li class="k8s">
<a href="#"> <i class="fa fa-gitlab"></i> <span class="nav-label">k8s管理</span></a>
<a href="#"> <i class="fa fa-keyboard-o"></i> <span class="nav-label">k8s管理</span></a>
<ul class="nav nav-second-level">
{% if perms.k8s.view_ecs %}
<li class="k8s-node-list">
<a href="{% url "k8s:k8s-node-list" %}">Node列表</a>
</li>
<li class="k8s-service-list">
<a href="{% url "k8s:k8s-service-list" %}">Service列表</a>
</li>
<li class="k8s-pod-list">
<a href="{% url "k8s:k8s-pod-list" %}">Pod列表</a>
</li>
{% endif %}
</ul>
</li>
{% endif %}
{% ifequal user.is_superuser 1 %}

View File

@@ -0,0 +1,148 @@
{% extends "base/base.html" %}
{% load asset_filter %}
{% load bootstrap4 %}
{% load static %}
{% block header-css %}
<style type="text/css">
.sort_both_png {
background: url('/static/img/sort_both.png') no-repeat right 1px center;
cursor: pointer;
}
.sort_asc_png {
background: url('/static/img/sort_asc.png') no-repeat right 1px center;
cursor: pointer;
}
.sort_desc_png {
background: url('/static/img/sort_desc.png') no-repeat right 1px center;
cursor: pointer;
}
</style>
{% endblock %}
{% block title %}K8S{% endblock %}
{% block page-content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12" id="split-right">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>Node列表</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="table-responsive">
<form class="form-horizontal "
method="post">
{% csrf_token %}
<table class="table table-striped table-bordered table-hover dataTables-example">
<thead>
<tr>
<th>服务名</th>
<th>状态</th>
<th>IP</th>
<th>版本</th>
<th>镜像</th>
<th>IP</th>
<th>IP</th>
</tr>
</thead>
<tbody>
{% for k,v in data.items %}
<tr class="gradeA" id="{{ row.id }}" name="{{ k }}">
<td class="center">
<div class="">{{ k }}</div>
</td>
<td class="center">
{% if v.status == "Ready" %}
<div class="btn btn-success btn-xs"> {{ v.status }}</div>
{% else %}
<div class="btn btn-danger btn-xs"> {{ v.status }}</div>
{% endif %}
</td>
<td class="center">
<div class="">{{ v.ip}}</div>
</td>
<td class="center">
<div class="">{{ v.kubelet_version}}</div>
</td>
<td class="center">
<div class="">{{ v.os_image}}</div>
</td>
<td class="center">
<div class="">{{ v.ip}}</div>
</td>
<td class="center">
<div class="">{{ v.ip}}</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% block footer-js %}
<script>
window.onload = function () { // 固定左边导航栏
$(".k8s").addClass("active");
$(".k8s-node-list").addClass("active");
};
</script>
{% endblock %}
{% endblock %}

View File

@@ -0,0 +1,75 @@
{% extends "base/base.html" %}
{% block title %}资产详情{% endblock %}
{% block header-css %}
{% load bootstrap4 %}
{% endblock %}
{% block page-content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a class="text-center"><i class="fa fa-laptop"></i>{{ name }}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-12" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<pre>{{ data }}
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% block footer-js %}
<script>
window.onload = function () { // 固定左边导航栏
$(".k8s").addClass("active");
$(".k8s-pod-list").addClass("active");
};
</script>
{% endblock %}
{% endblock %}

View File

@@ -69,6 +69,7 @@
<th>主机名</th>
<th>ip</th>
<th>命名空间</th>
<th>详情</th>
<th>操作</th>
</tr>
</thead>
@@ -87,23 +88,15 @@
<td class="center">
<div class="">{{ v.namespace}}</div>
</td>
<td class="center">
<a class="btn btn-success btn-xs "
href="{% url "k8s:k8s-pod-webssh" %}?namespace={{ v.namespace }}&name={{ k }}">webssh</a>
<td class="center">
<a class="btn btn-warning btn-xs " target="_blank"
href="{% url "k8s:k8s-pod-detail" %}?namespace={{ v.namespace }}&name={{ k }}">详情</a>
</td>
{# {% if perms.assets.view_ecs %}#}
{# <a class="btn btn-success btn-xs "#}
{# href="{% url "assets:ecs-detail" pk=row.id %}">详情</a>#}
{# {% endif %}#}
{# {% if perms.assets.change_ecs %}#}
{# <a class="btn btn-primary btn-xs "#}
{# href="{% url "assets:ecs-update" pk=row.id %}">编辑</a>#}
{# {% endif %}#}
{# {% if perms.assets.delete_ecs %}#}
{# <a class="btn btn-danger btn-xs ecs-delete"#}
{# href="#">删除</a>#}
{# {% endif %}#}
<td class="center">
<a class="btn btn-success btn-xs " target="_blank"
href="{% url "k8s:k8s-pod-webssh" %}?namespace={{ v.namespace }}&name={{ k }}">webssh</a>
</td>
</tr>
{% endfor %}

View File

@@ -0,0 +1,136 @@
{% extends "base/base.html" %}
{% load asset_filter %}
{% load bootstrap4 %}
{% load static %}
{% block header-css %}
<style type="text/css">
.sort_both_png {
background: url('/static/img/sort_both.png') no-repeat right 1px center;
cursor: pointer;
}
.sort_asc_png {
background: url('/static/img/sort_asc.png') no-repeat right 1px center;
cursor: pointer;
}
.sort_desc_png {
background: url('/static/img/sort_desc.png') no-repeat right 1px center;
cursor: pointer;
}
</style>
{% endblock %}
{% block title %}K8S{% endblock %}
{% block page-content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12" id="split-right">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>Service列表</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="table-responsive">
<form class="form-horizontal "
method="post">
{% csrf_token %}
<table class="table table-striped table-bordered table-hover dataTables-example">
<thead>
<tr>
<th>服务名</th>
<th>cluster_ip</th>
<th>类型</th>
<th>external_i_ps</th>
<th>target_port/port/node_port</th>
</tr>
</thead>
<tbody>
{% for k,v in data.items %}
<tr class="gradeA" id="{{ row.id }}" name="{{ k }}">
<td class="center">
<div class="">{{ k }}</div>
</td>
<td class="center">
<div class="">{{ v.cluster_ip }}</div>
</td>
<td class="center">
<div class="">{{ v.type}}</div>
</td>
<td class="center">
<div class="">{{ v.external_i_ps}}</div>
</td>
<td class="center">
<div class="">{% for i in v.port %}{{ i }} <br />{% endfor %}</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% block footer-js %}
<script>
window.onload = function () { // 固定左边导航栏
$(".k8s").addClass("active");
$(".k8s-service-list").addClass("active");
};
</script>
{% endblock %}
{% endblock %}