James Iter 9 лет назад
Родитель
Сommit
3c8127b87d
9 измененных файлов с 213 добавлено и 94 удалено
  1. 3 3
      config.json
  2. 2 0
      main.py
  3. 1 1
      models/event_processor.py
  4. 4 1
      models/guest_xml.py
  5. 2 0
      models/initialize.py
  6. 4 0
      models/rules.py
  7. 10 10
      route_table.py
  8. 4 0
      state_code.py
  9. 183 79
      views/guest.py

+ 3 - 3
config.json

@@ -27,11 +27,11 @@
   "SESSION_COOKIE_SECURE": false,
   "SECRET_KEY": "QSYI73re6x553wmcNwT9tk4OCNK9OUS9xNUulDShEcvRw00YhCKaqHhEGYOGKOSB",
   "PERMANENT_SESSION_LIFETIME": 604800,
-  "vm_create_queue": "Q:VMCreate",
-  "host_event_report_queue": "Q:HostEvent",
   "instruction_channel": "C:Instruction",
   "ip_available_set": "S:IP:Available",
   "ip_used_set": "S:IP:Used",
   "vnc_port_available_set": "S:VNCPort:Available",
-  "vnc_port_used_set": "S:VNCPort:Used"
+  "vnc_port_used_set": "S:VNCPort:Used",
+  "downstream_queue": "Q:Downstream",
+  "upstream_queue": "Q:Upstream"
 }

+ 2 - 0
main.py

@@ -21,6 +21,7 @@ from views.os_init_write import blueprints as os_init_write_blueprints
 from views.os_template import blueprint as os_template_blueprint
 from views.os_template import blueprints as os_template_blueprints
 from views.guest import blueprint as guest_blueprint
+from views.guest import blueprints as guest_blueprints
 from views.config import blueprint as config_blueprint
 from views.log import blueprint as log_blueprint
 from views.log import blueprints as log_blueprints
@@ -45,6 +46,7 @@ try:
     app.register_blueprint(os_template_blueprint)
     app.register_blueprint(os_template_blueprints)
     app.register_blueprint(guest_blueprint)
+    app.register_blueprint(guest_blueprints)
     app.register_blueprint(config_blueprint)
     app.register_blueprint(log_blueprint)
     app.register_blueprint(log_blueprints)

+ 1 - 1
models/event_processor.py

@@ -48,7 +48,7 @@ class EventProcessor(object):
                 return
 
             try:
-                host_log = db.r.lpop(app.config['host_event_report_queue'])
+                host_log = db.r.lpop(app.config['upstream_queue'])
 
                 if host_log is None:
                     time.sleep(1)

+ 4 - 1
models/guest_xml.py

@@ -1,5 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
+
+
 from models import Config
 from models import Guest
 
@@ -125,8 +127,9 @@ class GuestXML(object):
 
     def get_disks(self):
 
+        from initialize import dev_table
+
         disks = []
-        dev_table = ['vda', 'vdb', 'vdc', 'vdd']
 
         for i, disk in enumerate(self.disks):
             disks.append("""

+ 2 - 0
models/initialize.py

@@ -103,3 +103,5 @@ app.config = dict(app.config, **config)
 
 ji.index_state['branch'] = dict(ji.index_state['branch'], **own_state_branch)
 
+# sequence_device_node_mapping = ['vda', 'vdb', 'vdc', 'vdd']
+dev_table = ['vda', 'vdb', 'vdc', 'vdd']

+ 4 - 0
models/rules.py

@@ -40,6 +40,7 @@ class Rules(Enum):
     RSA_PUBLIC = (basestring, 'rsa_public')
 
     UUID = (basestring, 'uuid', (36, 36))
+    UUIDS = ('regex:^([\w-]{36})(,[\w-]{36})*$', 'uuids')
     CPU = (int, 'cpu')
     MEMORY = (int, 'memory')
     OS_TEMPLATE_ID = (int, 'os_template_id')
@@ -48,6 +49,9 @@ class Rules(Enum):
     NAME = (basestring, 'name')
     PASSWORD = (basestring, 'password')
     LEASE_TERM = (int, 'lease_term')
+    DESTINATION_HOST = (basestring, 'destination_host', (5, 64))
+    DEVICE_NODE_UUID = (basestring, 'device_node_uuid', (36, 36))
+    DISK_SIZE = ('regex:^\d{1,7}$', 'size')
 
     REMARK = (basestring, 'remark')
     LABEL = (basestring, 'label')

+ 10 - 10
route_table.py

@@ -43,18 +43,18 @@ add_rule(os_template.blueprints, '', view_func='os_template.r_get_by_filter', me
 # Guest操作
 # 创建虚拟机
 add_rule(guest.blueprint, '', view_func='guest.r_create', methods=['POST'])
-add_rule(guest.blueprint, '/_reboot/<uuid>', view_func='guest.r_reboot', methods=['PUT'])
-add_rule(guest.blueprint, '/_force_reboot/<uuid>', view_func='guest.r_force_reboot', methods=['PUT'])
-add_rule(guest.blueprint, '/_shutdown/<uuid>', view_func='guest.r_shutdown', methods=['PUT'])
-add_rule(guest.blueprint, '/_force_shutdown/<uuid>', view_func='guest.r_force_shutdown', methods=['PUT'])
-add_rule(guest.blueprint, '/_boot/<uuid>', view_func='guest.r_boot', methods=['PUT'])
-add_rule(guest.blueprint, '/_suspend/<uuid>', view_func='guest.r_suspend', methods=['PUT'])
-add_rule(guest.blueprint, '/_resume/<uuid>', view_func='guest.r_resume', methods=['PUT'])
-add_rule(guest.blueprint, '/_delete/<uuid>', view_func='guest.r_delete', methods=['PUT'])
-add_rule(guest.blueprint, '/_disk_resize/<uuid>', view_func='guest.r_disk_resize', methods=['PUT'])
+add_rule(guest.blueprints, '/_reboot/<uuids>', view_func='guest.r_reboot', methods=['PUT'])
+add_rule(guest.blueprints, '/_force_reboot/<uuids>', view_func='guest.r_force_reboot', methods=['PUT'])
+add_rule(guest.blueprints, '/_shutdown/<uuids>', view_func='guest.r_shutdown', methods=['PUT'])
+add_rule(guest.blueprints, '/_force_shutdown/<uuids>', view_func='guest.r_force_shutdown', methods=['PUT'])
+add_rule(guest.blueprints, '/_boot/<uuids>', view_func='guest.r_boot', methods=['PUT'])
+add_rule(guest.blueprints, '/_suspend/<uuids>', view_func='guest.r_suspend', methods=['PUT'])
+add_rule(guest.blueprints, '/_resume/<uuids>', view_func='guest.r_resume', methods=['PUT'])
+add_rule(guest.blueprints, '/_delete/<uuids>', view_func='guest.r_delete', methods=['PUT'])
+add_rule(guest.blueprint, '/_disk_resize/<device_node_uuid>/<size>', view_func='guest.r_disk_resize', methods=['PUT'])
 add_rule(guest.blueprint, '/_attach_disk/<uuid>', view_func='guest.r_attach_disk', methods=['PUT'])
 add_rule(guest.blueprint, '/_detach_disk/<uuid>', view_func='guest.r_detach_disk', methods=['PUT'])
-add_rule(guest.blueprint, '/_migrate/<uuid>', view_func='guest.r_migrate', methods=['PUT'])
+add_rule(guest.blueprint, '/_migrate/<uuids>/<destination_host>', view_func='guest.r_migrate', methods=['PUT'])
 
 # 日志查询
 # 系统模板操作

+ 4 - 0
state_code.py

@@ -37,6 +37,10 @@ own_state_branch = {
         'code': '41254',
         'zh-cn': u'起始的可用IP地址必须小于结束的可用IP地址'
     },
+    '41255': {
+        'code': '41255',
+        'zh-cn': u'磁盘大小超出范围'
+    },
     '50050': {
         'code': '50050',
         'zh-cn': u'MySQL 链接或执行出错'

+ 183 - 79
views/guest.py

@@ -10,7 +10,7 @@ from uuid import uuid4
 import jimit as ji
 
 from models import OSInitWrite
-from models.initialize import app
+from models.initialize import app, dev_table
 from models import Database as db
 from models import Config
 from models import GuestDisk
@@ -33,6 +33,12 @@ blueprint = Blueprint(
     url_prefix='/api/guest'
 )
 
+blueprints = Blueprint(
+    'guests',
+    __name__,
+    url_prefix='/api/guests'
+)
+
 
 @Utils.dumps2response
 def r_create():
@@ -138,6 +144,7 @@ def r_create():
                     replace('{DNS2}', config.dns2)
 
             create_vm_msg = {
+                'action': 'create_vm',
                 'uuid': guest.uuid,
                 'name': guest.name,
                 'glusterfs_volume': config.glusterfs_volume,
@@ -147,7 +154,7 @@ def r_create():
                 'password': guest.password,
                 'xml': guest_xml.get_domain()
             }
-            db.r.rpush(app.config['vm_create_queue'], json.dumps(create_vm_msg, ensure_ascii=False))
+            db.r.rpush(app.config['downstream_queue'], json.dumps(create_vm_msg, ensure_ascii=False))
 
         return ret
 
@@ -156,21 +163,23 @@ def r_create():
 
 
 @Utils.dumps2response
-def r_reboot(uuid):
+def r_reboot(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'reboot', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'reboot', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -181,21 +190,23 @@ def r_reboot(uuid):
 
 
 @Utils.dumps2response
-def r_force_reboot(uuid):
+def r_force_reboot(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'force_reboot', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'force_reboot', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -206,21 +217,23 @@ def r_force_reboot(uuid):
 
 
 @Utils.dumps2response
-def r_shutdown(uuid):
+def r_shutdown(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'shutdown', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'shutdown', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -231,21 +244,23 @@ def r_shutdown(uuid):
 
 
 @Utils.dumps2response
-def r_force_shutdown(uuid):
+def r_force_shutdown(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'force_shutdown', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'force_shutdown', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -256,21 +271,23 @@ def r_force_shutdown(uuid):
 
 
 @Utils.dumps2response
-def r_boot(uuid):
+def r_boot(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'boot', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'boot', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -281,21 +298,23 @@ def r_boot(uuid):
 
 
 @Utils.dumps2response
-def r_suspend(uuid):
+def r_suspend(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'suspend', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'suspend', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -306,21 +325,23 @@ def r_suspend(uuid):
 
 
 @Utils.dumps2response
-def r_resume(uuid):
+def r_resume(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'resume', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'resume', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -331,21 +352,23 @@ def r_resume(uuid):
 
 
 @Utils.dumps2response
-def r_delete(uuid):
+def r_delete(uuids):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'delete', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'delete', 'uuid': uuid}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -356,20 +379,22 @@ def r_delete(uuid):
 
 
 @Utils.dumps2response
-def r_disk_resize(uuid):
+def r_disk_resize(device_node_uuid, size):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.DEVICE_NODE_UUID.value,
+        Rules.DISK_SIZE.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'device_node_uuid': device_node_uuid, 'size': size})
 
-        guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        guest_disk = GuestDisk()
+        guest_disk.label = device_node_uuid
+        guest_disk.get_by('label')
 
-        message = {'action': 'disk-resize', 'uuid': uuid}
+        message = {'action': 'disk-resize', 'uuid': guest_disk.guest_uuid,
+                   'device_node': dev_table[guest_disk.sequence], 'size': size}
         Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
@@ -381,24 +406,101 @@ def r_disk_resize(uuid):
 
 
 @Utils.dumps2response
-def r_attach_disk(uuid):
+def r_create_disk(size):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.DISK_SIZE.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'size': size})
+
+        ret = dict()
+        ret['state'] = ji.Common.exchange_state(20000)
+
+        size = int(size)
+
+        if not isinstance(size, int) or size < 1:
+            ret['state'] = ji.Common.exchange_state(41255)
+            return ret
+
+        guest_disk = GuestDisk()
+        guest_disk.guest_uuid = ''
+        guest_disk.size = size
+        guest_disk.label = uuid4().__str__()
+        guest_disk.sequence = -1
+        guest_disk.format = 'qcow2'
+        guest_disk.create()
+
+        config = Config()
+        config.id = 1
+        config.get()
+
+        image_path = '/'.join(['DiskPool', guest_disk.label + '.' + guest_disk.format])
+
+        message = {'action': 'create_disk', 'glusterfs_volume': config.glusterfs_volume,
+                   'image_path': image_path, 'size': guest_disk.size}
+
+        db.r.rpush(app.config['downstream_queue'], json.dumps(message, ensure_ascii=False))
+
+        return ret
+
+    except ji.PreviewingError, e:
+        return json.loads(e.message)
+
+
+@Utils.dumps2response
+def r_attach_disk(uuid, size):
+
+    args_rules = [
+        Rules.UUID.value,
+        Rules.DISK_SIZE.value
+    ]
+
+    try:
+        ji.Check.previewing(args_rules, {'uuid': uuid, 'size': size})
 
         guest = Guest()
         guest.uuid = uuid
         guest.get_by('uuid')
 
-        message = {'action': 'attach-disk', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        guest_disk = GuestDisk()
+        guest_disk.guest_uuid = guest.uuid
+        disks, count = guest_disk.get_all()
+
+        guest_disk.size = int(size)
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
+
+        if not isinstance(guest_disk.size, int) or guest_disk.size < 1:
+            ret['state'] = ji.Common.exchange_state(41255)
+            return ret
+
+        config = Config()
+        config.id = 1
+        config.get()
+
+        guest_disk.label = uuid4().__str__()
+        guest_disk.sequence = count + 1
+        guest_disk.format = 'qcow2'
+        guest_disk.create()
+
+        xml = """
+            <disk type='network' device='disk'>
+                <driver name='qemu' type='qcow2' cache='none'/>
+                <source protocol='gluster' name='{0}/VMs/{1}/{2}.{3}'>
+                    <host name='127.0.0.1' port='24007'/>
+                </source>
+                <target dev='{4}' bus='virtio'/>
+            </disk>
+        """.format(config.glusterfs_volume, guest.name, guest_disk.label, guest_disk.format,
+                   dev_table[guest_disk.sequence])
+
+        message = {'action': 'attach_disk', 'uuid': uuid, 'xml': xml,
+                   'disk': {'label': guest_disk.label, 'size': guest_disk.size, 'format': guest_disk.format}}
+        Guest.emit_instruction(message=json.dumps(message))
+
         return ret
 
     except ji.PreviewingError, e:
@@ -419,7 +521,7 @@ def r_detach_disk(uuid):
         guest.uuid = uuid
         guest.get_by('uuid')
 
-        message = {'action': 'detach-disk', 'uuid': uuid}
+        message = {'action': 'detach_disk', 'uuid': uuid}
         Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
@@ -431,21 +533,24 @@ def r_detach_disk(uuid):
 
 
 @Utils.dumps2response
-def r_migrate(uuid):
+def r_migrate(uuids, destination_host):
 
     args_rules = [
-        Rules.UUID.value
+        Rules.UUIDS.value,
+        Rules.DESTINATION_HOST.value
     ]
 
     try:
-        ji.Check.previewing(args_rules, {'uuid': uuid})
+        ji.Check.previewing(args_rules, {'uuids': uuids, 'destination_host': destination_host})
 
         guest = Guest()
-        guest.uuid = uuid
-        guest.get_by('uuid')
+        for uuid in uuids.split(','):
+            guest.uuid = uuid
+            guest.get_by('uuid')
 
-        message = {'action': 'migrate', 'uuid': uuid}
-        Guest.emit_instruction(message=json.dumps(message))
+        for uuid in uuids.split(','):
+            message = {'action': 'migrate', 'uuid': uuid, 'duri': 'qemu+ssh://' + destination_host + '/system'}
+            Guest.emit_instruction(message=json.dumps(message))
 
         ret = dict()
         ret['state'] = ji.Common.exchange_state(20000)
@@ -454,4 +559,3 @@ def r_migrate(uuid):
     except ji.PreviewingError, e:
         return json.loads(e.message)
 
-