James Iter 7 years ago
parent
commit
866ceac51c

+ 5 - 1
docs/todo.md

@@ -96,5 +96,9 @@
 - [x] 替换热迁移时计算节点的主机名为 IP 地址
 - [ ] 加入批量执行脚本功能
 - [ ] 创建虚拟机时,加入高级功能,选择 IP 池,选择分组
-- [ ] 前后端分离,服务器端编译成二进制文件
+- [ ] 统计计算节点上所承载、运行的虚拟机实例数量,并通过超级链接能检索出相应虚拟机列表
+- [ ] IP 字段加入 link 功能,且允许用户定制。通过点击链接,直接打开目标页面
+- [ ] 支持 vlan 功能
+- [ ] 加入虚拟机排序功能
+- [ ] 企业版分叉
 

+ 43 - 0
jimvc/api/guest.py

@@ -889,6 +889,49 @@ def r_migrate(uuids, node_id):
         return json.loads(e.message)
 
 
+def r_change_vlan(uuids, vlan_id):
+    args_rules = [
+        Rules.UUIDS.value,
+        Rules.VLAN_ID_IN_URL.value
+    ]
+
+    try:
+        ji.Check.previewing(args_rules, {'uuids': uuids, 'vlan_id': vlan_id})
+
+        ret = dict()
+        ret['state'] = ji.Common.exchange_state(20000)
+
+        config = Config()
+        config.id = 1
+        config.get()
+
+        guest = Guest()
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
+
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
+
+            message = {
+                '_object': 'guest',
+                'action': 'change_vlan',
+                'uuid': uuid,
+                'node_id': guest.node_id,
+                'vlan_id': guest.vlan_id,
+                'vm_network': config.vm_network,
+                'passback_parameters': {'vlan_id': guest.vlan_id}
+            }
+
+            Utils.emit_instruction(message=json.dumps(message))
+
+        return ret
+
+    except ji.PreviewingError, e:
+        return json.loads(e.message)
+
+
 @Utils.dumps2response
 def r_get(uuids):
     ret = guest_base.get(ids=uuids, ids_rule=Rules.UUIDS.value, by_field='uuid')

+ 205 - 0
jimvc/api/vlan.py

@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+from flask import Blueprint, request
+import json
+import jimit as ji
+
+from jimvc.models import Utils, Guest
+from jimvc.models import Rules
+from jimvc.models import VLAN
+from jimvc.models import Host
+
+from base import Base
+
+
+__author__ = 'James Iter'
+__date__ = '2019-03-12'
+__contact__ = 'james.iter.cn@gmail.com'
+__copyright__ = '(c) 2019 by James Iter.'
+
+
+blueprint = Blueprint(
+    'api_vlan',
+    __name__,
+    url_prefix='/api/vlan'
+)
+
+blueprints = Blueprint(
+    'api_vlans',
+    __name__,
+    url_prefix='/api/vlans'
+)
+
+
+vlan_base = Base(the_class=VLAN, the_blueprint=blueprint, the_blueprints=blueprints)
+
+
+@Utils.dumps2response
+def r_create():
+
+    args_rules = [
+        Rules.VLAN_ID.value,
+        Rules.LABEL.value,
+        Rules.DESCRIPTION.value
+    ]
+
+    vlan = VLAN()
+    vlan.vlan_id = request.json.get('vlan_id')
+    vlan.label = request.json.get('label')
+    vlan.description = request.json.get('description', '')
+
+    try:
+        ji.Check.previewing(args_rules, vlan.__dict__)
+
+        ret = dict()
+        ret['state'] = ji.Common.exchange_state(20000)
+
+        vlan.create()
+        vlan.get()
+
+        # 取全部活着的 hosts
+        available_hosts = Host.get_available_hosts(nonrandom=None)
+        for host in available_hosts:
+            message = {
+                '_object': 'vlan',
+                'action': 'create',
+                'uuid': None,
+                'node_id': host['node_id'],
+                'vlan_id': vlan.vlan_id,
+                'passback_parameters': {'vlan_id': vlan.vlan_id}
+            }
+
+            Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
+
+        ret['data'] = vlan.__dict__
+        return ret
+    except ji.PreviewingError, e:
+        return json.loads(e.message)
+
+
+@Utils.dumps2response
+def r_update(ids):
+
+    ret = dict()
+    ret['state'] = ji.Common.exchange_state(20000)
+    ret['data'] = list()
+
+    args_rules = [
+        Rules.IDS.value
+    ]
+
+    if 'label' in request.json:
+        args_rules.append(
+            Rules.LABEL.value,
+        )
+
+    if 'description' in request.json:
+        args_rules.append(
+            Rules.DESCRIPTION.value,
+        )
+
+    if args_rules.__len__() < 2:
+        return ret
+
+    request.json['ids'] = ids
+
+    try:
+        ji.Check.previewing(args_rules, request.json)
+
+        vlan = VLAN()
+
+        # 检测所指定的 vlan 都存在
+        for _id in ids.split(','):
+            vlan.id = _id
+            vlan.get()
+
+        for _id in ids.split(','):
+            vlan.id = _id
+            vlan.get()
+            vlan.label = request.json.get('label', vlan.label)
+            vlan.description = request.json.get('description', vlan.description)
+
+            vlan.update()
+            vlan.get()
+
+            ret['data'].append(vlan.__dict__)
+
+        return ret
+    except ji.PreviewingError, e:
+        return json.loads(e.message)
+
+
+@Utils.dumps2response
+def r_get(ids):
+    return vlan_base.get(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
+
+
+@Utils.dumps2response
+def r_get_by_filter():
+    return vlan_base.get_by_filter()
+
+
+@Utils.dumps2response
+def r_content_search():
+    return vlan_base.content_search()
+
+
+@Utils.dumps2response
+def r_delete(ids):
+    args_rules = [
+        Rules.IDS.value
+    ]
+
+    request.json['ids'] = ids
+
+    try:
+        ji.Check.previewing(args_rules, request.json)
+
+        vlan = VLAN()
+
+        # 检测所指定的 vlan 都存在
+        for _id in ids.split(','):
+            vlan.id = _id
+            vlan.get()
+
+            guests, _ = Guest.get_by_filter(filter_str=':'.join(['vlan_id', 'eq', vlan.vlan_id]))
+
+            if guests.__len__() > 0:
+
+                guests_info = ''
+                for guest in guests:
+                    guests_info += guest['label'] + ',' + guest['uuid'] + '.'
+
+                ret = dict()
+                ret['state'] = ji.Common.exchange_state(41262)
+                ret['state']['sub']['zh-cn'] = ''.join([
+                    ret['state']['sub']['zh-cn'], u': Vlan ID: ', vlan.vlan_id, u': Guests: ', guests_info])
+
+                return ret
+
+        for _id in ids.split(','):
+            vlan.id = _id
+            vlan.get()
+
+            # 取全部活着的 hosts
+            available_hosts = Host.get_available_hosts(nonrandom=None)
+            for host in available_hosts:
+                message = {
+                    '_object': 'vlan',
+                    'action': 'delete',
+                    'uuid': None,
+                    'node_id': host['node_id'],
+                    'vlan_id': vlan.vlan_id,
+                    'passback_parameters': {'vlan_id': vlan.vlan_id}
+                }
+
+                Utils.emit_instruction(message=json.dumps(message, ensure_ascii=False))
+
+    except ji.PreviewingError, e:
+        return json.loads(e.message)
+
+    return vlan_base.delete(ids=ids, ids_rule=Rules.IDS.value, by_field='id')
+
+

+ 5 - 1
jimvc/models/__init__.py

@@ -136,6 +136,10 @@ from event_processor import (
     EventProcessor
 )
 
+from vlan import (
+    VLAN
+)
+
 
 __author__ = 'James Iter'
 __date__ = '2017/3/21'
@@ -150,6 +154,6 @@ __all__ = [
     'OSTemplateInitializeOperate', 'EventProcessor', 'ResponseState', 'GuestCPUMemory', 'GuestTraffic', 'GuestDiskIO',
     'HostCPUMemory', 'HostTraffic', 'HostDiskUsageIO', 'Host', 'Snapshot', 'SnapshotDiskMapping', 'OSTemplateImageKind',
     'GuestMigrateInfo', 'GuestCollectionPerformanceDataKind', 'HostCollectionPerformanceDataKind', 'StorageMode',
-    'OSTemplateInitializeOperateKind', 'dev_table', 'Project', 'Service', 'IPPool', 'ReservedIP', 'Token'
+    'OSTemplateInitializeOperateKind', 'dev_table', 'Project', 'Service', 'IPPool', 'ReservedIP', 'Token', 'VLAN'
 ]
 

+ 7 - 0
jimvc/models/event_processor.py

@@ -260,6 +260,13 @@ class EventProcessor(object):
                     cls.guest.memory = cls.message['message']['passback_parameters']['memory']
                     cls.guest.update()
 
+            elif action == 'change_vlan':
+                if state == ResponseState.success.value:
+                    cls.guest.uuid = uuid
+                    cls.guest.get_by('uuid')
+                    cls.guest.vlan_id = cls.message['message']['passback_parameters']['vlan_id']
+                    cls.guest.update()
+
         elif _object == 'disk':
             if action == 'create':
                 cls.disk.uuid = uuid

+ 6 - 4
jimvc/models/guest.py

@@ -42,6 +42,7 @@ class Guest(ORM):
         self.manage_network = None
         self.vnc_port = None
         self.vnc_password = None
+        self.vlan_id = -1
         self.xml = None
 
     @staticmethod
@@ -56,17 +57,18 @@ class Guest(ORM):
             'node_id': FilterFieldType.INT.value,
             'service_id': FilterFieldType.INT.value,
             'ip': FilterFieldType.STR.value,
-            'bandwidth': FilterFieldType.INT.value
+            'bandwidth': FilterFieldType.INT.value,
+            'vlan_id': FilterFieldType.INT.value
         }
 
     @staticmethod
     def get_allow_update_keywords():
-        return ['autostart','remark', 'cpu', 'memory', 'bandwidth', 'network', 'manage_network', 'vnc_password',
-                'service_id']
+        return ['autostart', 'remark', 'cpu', 'memory', 'bandwidth', 'network', 'manage_network', 'vnc_password',
+                'service_id', 'vlan_id']
 
     @staticmethod
     def get_allow_content_search_keywords():
-        return ['label', 'autostart', 'remark', 'node_id', 'ip']
+        return ['label', 'autostart', 'remark', 'node_id', 'ip', 'vlan_id']
 
 
 class Disk(ORM):

+ 2 - 2
jimvc/models/guest_xml.py

@@ -113,8 +113,8 @@ class GuestXML(object):
 
     def get_interface(self):
         return """
-            <interface type='network'>
-                <source network='{0}'/>
+            <interface type='bridge'>
+                <source bridge='{0}'/>
                 <model type='virtio'/>
                 <bandwidth>
                     <inbound average='{1}'/>

+ 4 - 0
jimvc/models/rules.py

@@ -134,3 +134,7 @@ class Rules(Enum):
     SERVICE_ID = (int, 'service_id')
     SERVICE_ID_IN_URL = (REG_NUMBER, 'service_id')
     SERVICES_ID = (REG_NUMBERS, 'services_id')
+
+    VLAN_ID = (int, 'vlan_id')
+    VLAN_ID_IN_URL = (REG_NUMBER, 'vlan_id')
+    VLANS_ID = (REG_NUMBERS, 'vlans_id')

+ 46 - 0
jimvc/models/vlan.py

@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+import jimit as ji
+
+from jimvc.models import FilterFieldType
+from jimvc.models import ORM
+
+
+__author__ = 'James Iter'
+__date__ = '2019-03-11'
+__contact__ = 'james.iter.cn@gmail.com'
+__copyright__ = '(c) 2019 by James Iter.'
+
+
+class VLAN(ORM):
+
+    _table_name = 'vlan'
+    _primary_key = 'id'
+
+    def __init__(self):
+        super(VLAN, self).__init__()
+        self.id = 0
+        self.vlan_id = None
+        self.label = ''
+        self.description = ''
+        self.create_time = ji.Common.tus()
+
+    @staticmethod
+    def get_filter_keywords():
+        return {
+            'id': FilterFieldType.INT.value,
+            'label': FilterFieldType.INT.value,
+            'vlan_id': FilterFieldType.INT.value
+        }
+
+    @staticmethod
+    def get_allow_update_keywords():
+        return []
+
+    @staticmethod
+    def get_allow_content_search_keywords():
+        return ['vlan_id', 'label', 'description']
+
+

+ 4 - 0
jimvc/state_code.py

@@ -66,6 +66,10 @@ own_state_branch = {
         'code': '41261',
         'zh-cn': u'Guest 状态不符合预期,期望 Guest 为关闭状态'
     },
+    '41262': {
+        'code': '41262',
+        'zh-cn': u'仍有 Guest 在此 Vlan 中,期望该 Vlan 中不存在任何 Guest'
+    },
     '50050': {
         'code': '50050',
         'zh-cn': u'MySQL 链接或执行出错'

+ 2 - 2
jimvc/themes/default/templates/guests_show.html

@@ -1694,7 +1694,7 @@
                     <th>状态</th>
                     <th>计算节点</th>
                     <th>配置</th>
-                    <th>IP</th>
+                    <th>网络</th>
                     <th width="150px;">密码&nbsp;&nbsp;<a href="#" class="glyph-icon icon-elusive-eye-off" onclick="change_display_password(this);" style="color: #014c8c;"></a></th>
                     <th>创建时间</th>
                     <th>操作</th>
@@ -1756,7 +1756,7 @@
                                 Gbps
                             {% endif %}
                     </td>
-                    <td>{{ item.ip }}</td>
+                    <td>{{ item.ip }}<br><a href="#">vlan: 2010</a></td>
                     <td><span class="unreal_password">*********</span><span class="real_password" style="display: none;">{{ item.password }}</span></td>
                     <td>{{ format_datetime_by_tus(item.create_time) }}</td>
                     <td>

+ 16 - 0
misc/init.sql

@@ -50,6 +50,7 @@ CREATE TABLE IF NOT EXISTS guest(
     manage_network VARCHAR(64) NOT NULL,
     vnc_port INT UNSIGNED NOT NULL,
     vnc_password VARCHAR(255) NOT NULL,
+    vlan_id INT NOT NULL DEFAULT -1,
     xml TEXT NOT NULL,
     PRIMARY KEY (id))
     ENGINE=InnoDB
@@ -61,6 +62,7 @@ ALTER TABLE guest ADD INDEX (autostart);
 ALTER TABLE guest ADD INDEX (node_id);
 ALTER TABLE guest ADD INDEX (service_id);
 ALTER TABLE guest ADD INDEX (ip);
+ALTER TABLE guest ADD INDEX (vlan_id);
 ALTER TABLE guest ADD INDEX (remark);
 
 
@@ -473,6 +475,20 @@ CREATE TABLE IF NOT EXISTS reserved_ip(
 ALTER TABLE reserved_ip ADD INDEX (ip);
 
 
+CREATE TABLE IF NOT EXISTS vlan(
+    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+    vlan_id INT NOT NULL UNIQUE,
+    label VARCHAR(255) NOT NULL,
+    description TEXT,
+    create_time BIGINT UNSIGNED NOT NULL,
+    PRIMARY KEY (id))
+    ENGINE=Innodb
+    DEFAULT CHARSET=utf8;
+
+ALTER TABLE vlan ADD INDEX (vlan_id);
+ALTER TABLE vlan ADD INDEX (label);
+
+
 INSERT INTO project (name, description, create_time) VALUES ('我的项目', '由 JimV 创建的默认项目。', UNIX_TIMESTAMP(NOW()) * 1000000);
 INSERT INTO service (project_id, name, description, create_time) VALUES (1, '服务组', '由 JimV 创建的默认服务组。', UNIX_TIMESTAMP(NOW()) * 1000000);
 

+ 18 - 0
misc/v0.8_to_v0.9/update.sql

@@ -0,0 +1,18 @@
+USE jimv;
+
+ALTER TABLE guest ADD COLUMN vlan_id INT NOT NULL DEFAULT -1;
+ALTER TABLE guest ADD INDEX (vlan_id);
+
+
+CREATE TABLE IF NOT EXISTS vlan(
+    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+    vlan_id INT NOT NULL UNIQUE,
+    label VARCHAR(255) NOT NULL,
+    description TEXT,
+    create_time BIGINT UNSIGNED NOT NULL,
+    PRIMARY KEY (id))
+    ENGINE=Innodb
+    DEFAULT CHARSET=utf8;
+
+ALTER TABLE vlan ADD INDEX (vlan_id);
+ALTER TABLE vlan ADD INDEX (label);