Add function to upload image from local/url in GUI 17/39117/6
authorchenjiankun <chenjiankun1@huawei.com>
Fri, 11 Aug 2017 09:26:22 +0000 (09:26 +0000)
committerchenjiankun <chenjiankun1@huawei.com>
Thu, 24 Aug 2017 03:29:25 +0000 (03:29 +0000)
JIRA: YARDSTICK-782

As user, we need to upload image from local/url.
If upload image from local, user need to choose local image, then
we will load it to openstack.
If upload image from url, we will download it and load it to openstack.

Change-Id: Ia9a42fda15a1dfc91476643635343a2f77a94a6b
Signed-off-by: chenjiankun <chenjiankun1@huawei.com>
16 files changed:
api/database/v2/handlers.py
api/database/v2/models.py
api/resources/v2/environments.py
api/resources/v2/images.py
api/server.py
api/urls.py
docker/nginx.sh
gui/app/scripts/controllers/image.controller.js
gui/app/scripts/controllers/main.js
gui/app/scripts/controllers/projectDetail.controller.js
gui/app/scripts/factory/main.factory.js
gui/app/views/modal/environmentDialog.html
gui/app/views/modal/imageDialog.html [new file with mode: 0644]
gui/app/views/podupload.html
gui/app/views/uploadImage.html
yardstick/common/constants.py

index 1bc32bf..e4f1dd6 100644 (file)
@@ -87,6 +87,11 @@ class V2ImageHandler(object):
             raise ValueError
         return image
 
+    def delete_by_uuid(self, uuid):
+        image = self.get_by_uuid(uuid)
+        db_session.delete(image)
+        db_session.commit()
+
 
 class V2PodHandler(object):
 
index 1e85559..59dab3e 100644 (file)
@@ -48,9 +48,6 @@ class V2Image(Base):
     name = Column(String(30))
     description = Column(Text)
     environment_id = Column(String(30))
-    size = Column(String(30))
-    status = Column(String(30))
-    time = Column(DateTime)
 
 
 class V2Container(Base):
index f021a3c..158e98b 100644 (file)
@@ -35,6 +35,9 @@ class V2Environments(ApiResource):
             container_info = e['container_id']
             e['container_id'] = jsonutils.loads(container_info) if container_info else {}
 
+            image_id = e['image_id']
+            e['image_id'] = image_id.split(',') if image_id else []
+
         data = {
             'environments': environments
         }
@@ -78,8 +81,13 @@ class V2Environment(ApiResource):
             return result_handler(consts.API_ERROR, 'no such environment id')
 
         environment = change_obj_to_dict(environment)
+
         container_id = environment['container_id']
         environment['container_id'] = jsonutils.loads(container_id) if container_id else {}
+
+        image_id = environment['image_id']
+        environment['image_id'] = image_id.split(',') if image_id else []
+
         return result_handler(consts.API_SUCCESS, {'environment': environment})
 
     def delete(self, environment_id):
index 8359e10..0c36a0a 100644 (file)
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 import logging
-import subprocess
+import os
+import uuid
 import threading
+import requests
+import datetime
 
 from api import ApiResource
+from api.database.v2.handlers import V2ImageHandler
+from api.database.v2.handlers import V2EnvironmentHandler
 from yardstick.common.utils import result_handler
 from yardstick.common.utils import source_env
 from yardstick.common.utils import change_obj_to_dict
 from yardstick.common.openstack_utils import get_nova_client
+from yardstick.common.openstack_utils import get_glance_client
 from yardstick.common import constants as consts
 
 LOG = logging.getLogger(__name__)
 LOG.setLevel(logging.DEBUG)
 
+IMAGE_MAP = {
+    'yardstick-image': {
+        'path': os.path.join(consts.IMAGE_DIR, 'yardstick-image.img'),
+        'url': 'http://artifacts.opnfv.org/yardstick/images/yardstick-image.img'
+    },
+    'Ubuntu-16.04': {
+        'path': os.path.join(consts.IMAGE_DIR, 'xenial-server-cloudimg-amd64-disk1.img'),
+        'url': 'cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img'
+    },
+    'cirros-0.3.5': {
+        'path': os.path.join(consts.IMAGE_DIR, 'cirros-0.3.5-x86_64-disk.img'),
+        'url': 'http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img'
+    }
+}
+
 
 class V2Images(ApiResource):
 
     def get(self):
         try:
             source_env(consts.OPENRC)
-        except:
+        except Exception:
             return result_handler(consts.API_ERROR, 'source openrc error')
 
         nova_client = get_nova_client()
         try:
             images_list = nova_client.images.list()
-        except:
+        except Exception:
             return result_handler(consts.API_ERROR, 'get images error')
         else:
-            images = [self.get_info(change_obj_to_dict(i)) for i in images_list]
-            status = 1 if all(i['status'] == 'ACTIVE' for i in images) else 0
-            if not images:
-                status = 0
+            images = {i.name: self.get_info(change_obj_to_dict(i)) for i in images_list}
 
-        return result_handler(consts.API_SUCCESS, {'status': status, 'images': images})
+        return result_handler(consts.API_SUCCESS, {'status': 1, 'images': images})
 
     def post(self):
         return self._dispatch_post()
 
     def get_info(self, data):
+        try:
+            size = data['OS-EXT-IMG-SIZE:size']
+        except KeyError:
+            size = None
+        else:
+            size = float(size) / 1024 / 1024
+
         result = {
             'name': data.get('name', ''),
-            'size': data.get('OS-EXT-IMG-SIZE:size', ''),
-            'status': data.get('status', ''),
-            'time': data.get('updated', '')
+            'discription': data.get('description', ''),
+            'size': size,
+            'status': data.get('status'),
+            'time': data.get('updated')
         }
         return result
 
     def load_image(self, args):
-        thread = threading.Thread(target=self._load_images)
+        try:
+            image_name = args['name']
+        except KeyError:
+            return result_handler(consts.API_ERROR, 'image name must provided')
+
+        if image_name not in IMAGE_MAP:
+            return result_handler(consts.API_ERROR, 'wrong image name')
+
+        thread = threading.Thread(target=self._do_load_image, args=(image_name,))
         thread.start()
+        return result_handler(consts.API_SUCCESS, {'image': image_name})
+
+    def upload_image(self, args):
+        try:
+            image_file = args['file']
+        except KeyError:
+            return result_handler(consts.API_ERROR, 'file must be provided')
+
+        try:
+            environment_id = args['environment_id']
+        except KeyError:
+            return result_handler(consts.API_ERROR, 'environment_id must be provided')
+
+        try:
+            uuid.UUID(environment_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'invalid environment id')
+
+        environment_handler = V2EnvironmentHandler()
+        try:
+            environment = environment_handler.get_by_uuid(environment_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'no such environment')
+
+        file_path = os.path.join(consts.IMAGE_DIR, image_file.filename)
+        LOG.info('saving file')
+        image_file.save(file_path)
+
+        LOG.info('loading image')
+        self._load_image(image_file.filename, file_path)
+
+        LOG.info('creating image in DB')
+        image_handler = V2ImageHandler()
+        image_id = str(uuid.uuid4())
+        image_init_data = {
+            'uuid': image_id,
+            'name': image_file.filename,
+            'environment_id': environment_id
+        }
+        image_handler.insert(image_init_data)
+
+        LOG.info('update image in environment')
+        if environment.image_id:
+            image_list = environment.image_id.split(',')
+            image_list.append(image_id)
+            new_image_id = ','.join(image_list)
+        else:
+            new_image_id = image_id
+
+        environment_handler.update_attr(environment_id, {'image_id': new_image_id})
+
+        return result_handler(consts.API_SUCCESS, {'uuid': image_id})
+
+    def upload_image_by_url(self, args):
+        try:
+            url = args['url']
+        except KeyError:
+            return result_handler(consts.API_ERROR, 'url must be provided')
+
+        try:
+            environment_id = args['environment_id']
+        except KeyError:
+            return result_handler(consts.API_ERROR, 'environment_id must be provided')
+
+        try:
+            uuid.UUID(environment_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'invalid environment id')
+
+        environment_handler = V2EnvironmentHandler()
+        try:
+            environment = environment_handler.get_by_uuid(environment_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'no such environment')
+
+        thread = threading.Thread(target=self._do_upload_image_by_url, args=(url,))
+        thread.start()
+
+        file_name = url.split('/')[-1]
+
+        LOG.info('creating image in DB')
+        image_handler = V2ImageHandler()
+        image_id = str(uuid.uuid4())
+        image_init_data = {
+            'uuid': image_id,
+            'name': file_name,
+            'environment_id': environment_id
+        }
+        image_handler.insert(image_init_data)
+
+        LOG.info('update image in environment')
+        if environment.image_id:
+            image_list = environment.image_id.split(',')
+            image_list.append(image_id)
+            new_image_id = ','.join(image_list)
+        else:
+            new_image_id = image_id
+
+        environment_handler.update_attr(environment_id, {'image_id': new_image_id})
+
+        return result_handler(consts.API_SUCCESS, {'uuid': image_id})
+
+    def delete_image(self, args):
+        try:
+            image_name = args['name']
+        except KeyError:
+            return result_handler(consts.API_ERROR, 'image name must provided')
+
+        if image_name not in IMAGE_MAP:
+            return result_handler(consts.API_ERROR, 'wrong image name')
+
+        glance_client = get_glance_client()
+        try:
+            image = next((i for i in glance_client.images.list() if i.name == image_name))
+        except StopIteration:
+            return result_handler(consts.API_ERROR, 'can not find image')
+
+        glance_client.images.delete(image.id)
+
         return result_handler(consts.API_SUCCESS, {})
 
-    def _load_images(self):
+    def _do_upload_image_by_url(self, url):
+        file_name = url.split('/')[-1]
+        path = os.path.join(consts.IMAGE_DIR, file_name)
+
+        LOG.info('download image')
+        self._download_image(url, path)
+
+        LOG.info('loading image')
+        self._load_image(file_name, path)
+
+    def _do_load_image(self, image_name):
+        if not os.path.exists(IMAGE_MAP[image_name]['path']):
+            self._download_image(IMAGE_MAP[image_name]['url'],
+                                 IMAGE_MAP[image_name]['path'])
+
+        self._load_image(image_name, IMAGE_MAP[image_name]['path'])
+
+    def _load_image(self, image_name, image_path):
         LOG.info('source openrc')
         source_env(consts.OPENRC)
 
-        LOG.info('clean images')
-        cmd = [consts.CLEAN_IMAGES_SCRIPT]
-        p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                             cwd=consts.REPOS_DIR)
-        _, err = p.communicate()
-        if p.returncode != 0:
-            LOG.error('clean image failed: %s', err)
-
-        LOG.info('load images')
-        cmd = [consts.LOAD_IMAGES_SCRIPT]
-        p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                             cwd=consts.REPOS_DIR)
-        _, err = p.communicate()
-        if p.returncode != 0:
-            LOG.error('load image failed: %s', err)
+        LOG.info('load image')
+        glance_client = get_glance_client()
+        image = glance_client.images.create(name=image_name,
+                                            visibility='public',
+                                            disk_format='qcow2',
+                                            container_format='bare')
+        with open(image_path, 'rb') as f:
+            glance_client.images.upload(image.id, f)
 
         LOG.info('Done')
+
+    def _download_image(self, url, path):
+        start = datetime.datetime.now().replace(microsecond=0)
+
+        LOG.info('download image from: %s', url)
+        self._download_file(url, path)
+
+        end = datetime.datetime.now().replace(microsecond=0)
+        LOG.info('download image success, total: %s s', end - start)
+
+    def _download_handler(self, start, end, url, filename):
+
+        headers = {'Range': 'bytes=%d-%d' % (start, end)}
+        r = requests.get(url, headers=headers, stream=True)
+
+        with open(filename, "r+b") as fp:
+            fp.seek(start)
+            fp.tell()
+            fp.write(r.content)
+
+    def _download_file(self, url, path, num_thread=5):
+
+        r = requests.head(url)
+        try:
+            file_size = int(r.headers['content-length'])
+        except Exception:
+            return
+
+        with open(path, 'wb') as f:
+            f.truncate(file_size)
+
+        thread_list = []
+        part = file_size // num_thread
+        for i in range(num_thread):
+            start = part * i
+            end = start + part if i != num_thread - 1 else file_size
+
+            kwargs = {'start': start, 'end': end, 'url': url, 'filename': path}
+            t = threading.Thread(target=self._download_handler, kwargs=kwargs)
+            t.setDaemon(True)
+            t.start()
+            thread_list.append(t)
+
+        for t in thread_list:
+            t.join()
+
+
+class V2Image(ApiResource):
+    def get(self, image_id):
+        try:
+            uuid.UUID(image_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'invalid image id')
+
+        image_handler = V2ImageHandler()
+        try:
+            image = image_handler.get_by_uuid(image_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'no such image id')
+
+        nova_client = get_nova_client()
+        images = nova_client.images.list()
+        try:
+            image = next((i for i in images if i.name == image.name))
+        except StopIteration:
+            pass
+
+        return_image = self.get_info(change_obj_to_dict(image))
+        return_image['id'] = image_id
+
+        return result_handler(consts.API_SUCCESS, {'image': return_image})
+
+    def delete(self, image_id):
+        try:
+            uuid.UUID(image_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'invalid image id')
+
+        image_handler = V2ImageHandler()
+        try:
+            image = image_handler.get_by_uuid(image_id)
+        except ValueError:
+            return result_handler(consts.API_ERROR, 'no such image id')
+
+        LOG.info('delete image in openstack')
+        glance_client = get_glance_client()
+        try:
+            image_o = next((i for i in glance_client.images.list() if i.name == image.name))
+        except StopIteration:
+            return result_handler(consts.API_ERROR, 'can not find image')
+
+        glance_client.images.delete(image_o.id)
+
+        LOG.info('delete image in environment')
+        environment_id = image.environment_id
+        environment_handler = V2EnvironmentHandler()
+        environment = environment_handler.get_by_uuid(environment_id)
+        image_list = environment.image_id.split(',')
+        image_list.remove(image_id)
+        environment_handler.update_attr(environment_id, {'image_id': ','.join(image_list)})
+
+        LOG.info('delete image in DB')
+        image_handler.delete_by_uuid(image_id)
+
+        return result_handler(consts.API_SUCCESS, {'image': image_id})
+
+    def get_info(self, data):
+        try:
+            size = data['OS-EXT-IMG-SIZE:size']
+        except KeyError:
+            size = None
+        else:
+            size = float(size) / 1024 / 1024
+
+        result = {
+            'name': data.get('name', ''),
+            'description': data.get('description', ''),
+            'size': size,
+            'status': data.get('status'),
+            'time': data.get('updated')
+        }
+        return result
index 158b8a5..37a1ab6 100644 (file)
@@ -35,6 +35,7 @@ except ImportError:
 LOG = logging.getLogger(__name__)
 
 app = Flask(__name__)
+app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024 * 1024
 
 Swagger(app)
 
index 83cf4da..9b0040b 100644 (file)
@@ -36,6 +36,7 @@ urlpatterns = [
 
     Url('/api/v2/yardstick/images', 'v2_images'),
     Url('/api/v2/yardstick/images/action', 'v2_images'),
+    Url('/api/v2/yardstick/images/<image_id>', 'v2_image'),
 
     Url('/api/v2/yardstick/containers', 'v2_containers'),
     Url('/api/v2/yardstick/containers/action', 'v2_containers'),
index 74009f5..1ac1d3f 100755 (executable)
@@ -20,6 +20,7 @@ server {
     index  index.htm index.html;
     location / {
         include uwsgi_params;
+        client_max_body_size    2000m;
         uwsgi_pass unix:///var/run/yardstick.sock;
     }
 
index f6c9159..d7a7edf 100644 (file)
 'use strict';
 
 angular.module('yardStickGui2App')
-    .controller('ImageController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', '$location', '$interval',
-        function($scope, $state, $stateParams, mainFactory, Upload, toaster, $location, $interval) {
+    .controller('ImageController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', '$location', '$interval', 'ngDialog',
+        function($scope, $state, $stateParams, mainFactory, Upload, toaster, $location, $interval, ngDialog) {
 
 
             init();
-            $scope.showloading = false;
-            $scope.ifshowStatus = 0;
 
             function init() {
+                $scope.showloading = false;
+                $scope.ifshowStatus = 0;
+
+                $scope.yardstickImage = [
+                    {
+                        'name': 'yardstick-image',
+                        'description': '',
+                        'size': 'N/A',
+                        'status': 'N/A',
+                        'time': 'N/A'
+                    },
+                    {
+                        'name': 'Ubuntu-16.04',
+                        'description': '',
+                        'size': 'N/A',
+                        'status': 'N/A',
+                        'time': 'N/A'
+                    },
+                    {
+                        'name': 'cirros-0.3.5',
+                        'description': '',
+                        'size': 'N/A',
+                        'status': 'N/A',
+                        'time': 'N/A'
+                    }
+                ];
+                $scope.customImage = [];
 
 
                 $scope.uuid = $stateParams.uuid;
-                $scope.uploadImage = uploadImage;
-                getItemIdDetail();
-                getImageListSimple();
+                $scope.showloading = false;
+                $scope.url = null;
+                $scope.environmentInfo = null;
+
+                getYardstickImageList();
+                getCustomImageList(function(image, image_id){});
             }
 
-            function getItemIdDetail() {
+            function getYardstickImageList(){
+                mainFactory.ImageList().get({}).$promise.then(function(response){
+                    if(response.status == 1){
+                        angular.forEach($scope.yardstickImage, function(ele, index){
+                            if(typeof(response.result.images[ele.name]) != 'undefined'){
+                                $scope.yardstickImage[index] = response.result.images[ele.name];
+                            }
+                        });
+                    }else{
+                        mainFactory.errorHandler1(response);
+                    }
+                }, function(response){
+                    mainFactory.errorHandler2(response);
+                });
+            }
+
+            function getCustomImageList(func){
                 mainFactory.ItemDetail().get({
                     'envId': $stateParams.uuid
                 }).$promise.then(function(response) {
-                    if (response.status == 1) {
-                        $scope.baseElementInfo = response.result.environment;
-
-
-                    } else {
-                        toaster.pop({
-                            type: 'error',
-                            title: 'fail',
-                            body: response.error_msg,
-                            timeout: 3000
+                    if(response.status == 1){
+                        $scope.environmentInfo = response.result.environment;
+                        $scope.customImage = [];
+                        angular.forEach(response.result.environment.image_id, function(ele){
+                            mainFactory.getImage().get({'imageId': ele}).$promise.then(function(responseData){
+                                if(responseData.status == 1){
+                                    $scope.customImage.push(responseData.result.image);
+                                    func(responseData.result.image, ele);
+                                }else{
+                                    mainFactory.errorHandler1(responseData);
+                                }
+                            }, function(errorData){
+                                mainFactory.errorHandler2(errorData);
+                            });
                         });
+                    }else{
+                        mainFactory.errorHandler1(response);
                     }
-                }, function(error) {
-                    toaster.pop({
-                        type: 'error',
-                        title: 'fail',
-                        body: 'unknow error',
-                        timeout: 3000
-                    });
-                })
+                }, function(response){
+                    mainFactory.errorHandler2(response);
+                });
             }
 
-            function getImageListSimple() {
-
-                mainFactory.ImageList().get({}).$promise.then(function(response) {
-                    if (response.status == 1) {
-                        $scope.imageListData = response.result.images;
-                        // $scope.imageStatus = response.result.status;
-
-                    } else {
-                        toaster.pop({
-                            type: 'error',
-                            title: 'get data failed',
-                            body: 'please retry',
-                            timeout: 3000
-                        });
-                    }
-                }, function(error) {
-                    toaster.pop({
-                        type: 'error',
-                        title: 'get data failed',
-                        body: 'please retry',
-                        timeout: 3000
+            $scope.loadYardstickImage = function(image_name){
+
+                var updateImageTask = $interval(updateYardstickImage, 10000);
+
+                function updateYardstickImage(){
+                    mainFactory.ImageList().get({}).$promise.then(function(responseData){
+                        if(responseData.status == 1){
+                            if(typeof(responseData.result.images[image_name]) != 'undefined' && responseData.result.images[image_name].status == 'ACTIVE'){
+                                angular.forEach($scope.yardstickImage, function(ele, index){
+                                    if(ele.name == image_name){
+                                        $scope.yardstickImage[index] = responseData.result.images[ele.name];
+                                    }
+                                });
+                                $interval.cancel(updateImageTask);
+                            }
+                        }else{
+                            mainFactory.errorHandler1(responseData);
+                        }
+                    },function(errorData){
+                        mainFactory.errorHandler2(errorData);
                     });
-                })
-            }
+                }
 
+                mainFactory.uploadImage().post({'action': 'load_image', 'args': {'name': image_name}}).$promise.then(function(response){
+                },function(response){
+                    mainFactory.errorHandler2(response);
+                });
+            }
 
-            function getImageList() {
-                if ($scope.intervalImgae != undefined) {
-                    $interval.cancel($scope.intervalImgae);
-                }
-                mainFactory.ImageList().get({}).$promise.then(function(response) {
-                    if (response.status == 1) {
-                        $scope.imageListData = response.result.images;
-                        $scope.imageStatus = response.result.status;
-
-                        if ($scope.imageStatus == 0) {
-                            $scope.intervalImgae = $interval(function() {
-                                getImageList();
-                            }, 5000);
-                        } else if ($scope.intervalImgae != undefined) {
-                            $interval.cancel($scope.intervalImgae);
+            $scope.deleteYardstickImage = function(image_name){
+
+                var updateImageTask = $interval(updateYardstickImage, 10000);
+
+                function updateYardstickImage(){
+                    mainFactory.ImageList().get({}).$promise.then(function(response){
+                        if(response.status == 1){
+                            if(typeof(response.result.images[image_name]) == 'undefined'){
+                                angular.forEach($scope.yardstickImage, function(ele, index){
+                                    if(ele.name == image_name){
+                                        $scope.yardstickImage[index].size = 'N/A';
+                                        $scope.yardstickImage[index].status = 'N/A';
+                                        $scope.yardstickImage[index].time = 'N/A';
+                                    }
+                                });
+                                $interval.cancel(updateImageTask);
+                            }
+                        }else{
+                            mainFactory.errorHandler1(response);
                         }
+                    },function(response){
+                        mainFactory.errorHandler2(response);
+                    });
+                }
 
-                    } else {
-                        toaster.pop({
-                            type: 'error',
-                            title: 'get data failed',
-                            body: 'please retry',
-                            timeout: 3000
+                mainFactory.uploadImage().post({'action': 'delete_image', 'args': {'name': image_name}}).$promise.then(function(response){
+                },function(response){
+                    mainFactory.errorHandler2(response);
+                });
+            }
+
+            $scope.uploadCustomImageByUrl = function(url){
+                mainFactory.uploadImageByUrl().post({
+                    'action': 'upload_image_by_url',
+                    'args': {
+                        'environment_id': $stateParams.uuid,
+                        'url': url
+                    }
+                }).$promise.then(function(response){
+                    if(response.status == 1){
+                        var updateImageTask = $interval(getCustomImageList, 30000, 10, true, function(image, image_id){
+                            if(image_id == response.result.uuid && image.status == 'ACTIVE'){
+                                $interval.cancel(updateImageTask);
+                            }
                         });
+                        ngDialog.close();
+                    }else{
+                        mainFactory.errorHandler1(response);
                     }
-                }, function(error) {
-                    toaster.pop({
-                        type: 'error',
-                        title: 'get data failed',
-                        body: 'please retry',
-                        timeout: 3000
-                    });
-                })
+                }, function(response){
+                    mainFactory.errorHandler2(response);
+                });
             }
 
-            function uploadImage() {
-                $scope.imageStatus = 0;
-                $interval.cancel($scope.intervalImgae);
-                $scope.ifshowStatus = 1;
+            $scope.uploadCustomImage = function($file, $invalidFiles) {
                 $scope.showloading = true;
-                mainFactory.uploadImage().post({
-                    'action': 'load_image',
-                    'args': {
-                        'environment_id': $scope.uuid
 
-                    }
-                }).$promise.then(function(response) {
+                $scope.displayImageFile = $file;
+                Upload.upload({
+                    url: Base_URL + '/api/v2/yardstick/images',
+                    data: { file: $file, 'environment_id': $scope.uuid, 'action': 'upload_image' }
+                }).then(function(response) {
+
                     $scope.showloading = false;
-                    if (response.status == 1) {
+                    if (response.data.status == 1) {
+
                         toaster.pop({
                             type: 'success',
-                            title: 'create success',
+                            title: 'upload success',
                             body: 'you can go next step',
                             timeout: 3000
                         });
-                        setTimeout(function() {
-                            getImageList();
-                        }, 10000);
 
-                    } else {
-                        toaster.pop({
-                            type: 'error',
-                            title: 'failed',
-                            body: 'something wrong',
-                            timeout: 3000
+                        var updateImageTask = $interval(getCustomImageList, 10000, 10, true, function(image, image_id){
+                            if(image_id == response.data.result.uuid && image.status == 'ACTIVE'){
+                                $interval.cancel(updateImageTask);
+                            }
                         });
+                    }else{
+                        mainFactory.errorHandler1(response);
+                    }
 
+                }, function(response) {
+                    $scope.uploadfile = null;
+                    mainFactory.errorHandler2(response);
+                })
+            }
+
+            $scope.deleteCustomImage = function(image_id){
+                mainFactory.deleteImage().delete({'imageId': image_id}).$promise.then(function(response){
+                    if(response.status == 1){
+                        $interval(getCustomImageList, 10000, 5, true, function(image, image_id){
+                        });
+                    }else{
+                        mainFactory.errorHandler2(response);
                     }
-                }, function(error) {
-                    toaster.pop({
-                        type: 'error',
-                        title: 'failed',
-                        body: 'something wrong',
-                        timeout: 3000
-                    });
+                }, function(response){
+                    mainFactory.errorHandler2(response);
+                });
+            }
+
+            $scope.openImageDialog = function(){
+                $scope.url = null;
+                ngDialog.open({
+                    preCloseCallback: function(value) {
+                    },
+                    template: 'views/modal/imageDialog.html',
+                    scope: $scope,
+                    className: 'ngdialog-theme-default',
+                    width: 950,
+                    showClose: true,
+                    closeByDocument: false
                 })
             }
 
@@ -158,9 +243,5 @@ angular.module('yardStickGui2App')
                 $state.go('app.podUpload', { uuid: $scope.uuid });
             }
 
-
-
-
-
         }
     ]);
index ab76bf0..ceec83f 100644 (file)
@@ -15,7 +15,7 @@ angular.module('yardStickGui2App')
             $scope.showImage = null;
             $scope.showContainer = null;
             $scope.showNextOpenRc = null;
-            $scope.showNextPod = null;
+            $scope.showNextPod = 1;
             $scope.displayContainerInfo = [];
             $scope.containerList = [{ value: 'create_influxdb', name: "InfluxDB" }, { value: 'create_grafana', name: "Grafana" }]
 
@@ -51,7 +51,6 @@ angular.module('yardStickGui2App')
                 $scope.chooseResult = chooseResult;
 
                 getEnvironmentList();
-                // getImageList();
 
             }
 
@@ -85,7 +84,7 @@ angular.module('yardStickGui2App')
             }
 
             $scope.goToImage = function goToImage() {
-                getImageListSimple();
+                getImageList();
                 $scope.showImage = 1;
             }
             $scope.goToPod = function goToPod() {
@@ -290,7 +289,7 @@ angular.module('yardStickGui2App')
                 $scope.showImage = null;
                 $scope.showContainer = null;
                 $scope.showNextOpenRc = null;
-                $scope.showNextPod = null;
+                $scope.showNextPod = 1;
                 $scope.displayContainerInfo = [];
 
                 $scope.displayPodFile = null;
@@ -308,7 +307,6 @@ angular.module('yardStickGui2App')
                 ngDialog.open({
                     preCloseCallback: function(value) {
                         getEnvironmentList();
-                        // getImageList();
                     },
                     template: 'views/modal/environmentDialog.html',
                     scope: $scope,
@@ -479,106 +477,97 @@ angular.module('yardStickGui2App')
                 })
             }
 
-            $scope.uploadImage = function uploadImage() {
-                $scope.imageStatus = 0;
-                $scope.showImageStatus = 1;
-                $scope.showloading = true;
-                mainFactory.uploadImage().post({
-                    'action': 'load_image',
-                    'args': {
-                        'environment_id': $scope.uuid
+            $scope.yardstickImage = {
+                'yardstick-image': {
+                    'name': 'yardstick-image',
+                    'description': '',
+                    'status': 'N/A'
+                },
+                'Ubuntu-16.04': {
+                    'name': 'Ubuntu-16.04',
+                    'description': '',
+                    'status': 'N/A'
+                },
+                'cirros-0.3.5': {
+                    'name': 'cirros-0.3.5',
+                    'description': '',
+                    'status': 'N/A'
+                }
+            };
 
-                    }
-                }).$promise.then(function(response) {
-                    $scope.showloading = false;
-                    if (response.status == 1) {
-                        toaster.pop({
-                            type: 'success',
-                            title: 'create success',
-                            body: 'you can go next step',
-                            timeout: 3000
-                        });
-                        setTimeout(function() {
-                            getImageList();
-                        }, 10000);
-                        $scope.showNextPod = 1;
+            $scope.selectImageList = [];
 
-                    } else {
-                        toaster.pop({
-                            type: 'error',
-                            title: 'failed',
-                            body: 'something wrong',
-                            timeout: 3000
-                        });
+            $scope.selectImage = function(name){
+                $scope.selectImageList.push(name);
+            }
 
-                    }
-                }, function(error) {
-                    toaster.pop({
-                        type: 'error',
-                        title: 'failed',
-                        body: 'something wrong',
-                        timeout: 3000
-                    });
-                })
+            $scope.unselectImage = function(name){
+                var index = $scope.selectImageList.indexOf(name);
+                $scope.selectImageList.splice(index, 1);
             }
 
-            function getImageList() {
-                if ($scope.intervalImgae != undefined) {
-                    $interval.cancel($scope.intervalImgae);
-                }
-                mainFactory.ImageList().get({}).$promise.then(function(response) {
-                    if (response.status == 1) {
-                        $scope.imageListData = response.result.images;
-                        $scope.imageStatus = response.result.status;
+            $scope.uploadImage = function() {
+                $scope.imageStatus = 0;
+                $scope.showImageStatus = 1;
+                $scope.showloading = true;
 
-                        if ($scope.imageStatus == 0) {
-                            $scope.intervalImgae = $interval(function() {
-                                getImageList();
-                            }, 5000);
-                        } else if ($scope.intervalImgae != undefined) {
-                            $interval.cancel($scope.intervalImgae);
+                var updateImageTask = $interval(function(){
+                    mainFactory.ImageList().get({}).$promise.then(function(response){
+                        if(response.status == 1){
+                            var isOk = true;
+                            angular.forEach($scope.selectImageList, function(ele){
+                                if(typeof(response.result.images[ele]) != 'undefined' && response.result.images[ele].status == 'ACTIVE'){
+                                    $scope.yardstickImage[ele] = response.result.images[ele];
+                                }else{
+                                    isOk = false;
+                                }
+                            });
+                            if(isOk){
+                                $interval.cancel(updateImageTask);
+                                $scope.imageStatus = 1;
+                            }
+                        }else{
+                            mainFactory.errorHandler1(response);
                         }
-
-                    } else {
-                        toaster.pop({
-                            type: 'error',
-                            title: 'get data failed',
-                            body: 'please retry',
-                            timeout: 3000
-                        });
-                    }
-                }, function(error) {
-                    toaster.pop({
-                        type: 'error',
-                        title: 'get data failed',
-                        body: 'please retry',
-                        timeout: 3000
+                    }, function(response){
+                        mainFactory.errorHandler2(response);
                     });
-                })
+                }, 10000);
+
+                angular.forEach($scope.selectImageList, function(ele){
+                    mainFactory.uploadImage().post({
+                        'action': 'load_image',
+                        'args': {
+                            'name': ele
+                        }
+                    }).$promise.then(function(response) {
+                        if(response.status == 1){
+                            $scope.showloading = false;
+                            $scope.showNextPod = 1;
+                        }else{
+                            mainFactory.errorHandler1(response);
+                        }
+                    }, function(response) {
+                        mainFactory.errorHandler2(response);
+                    })
+                });
             }
 
-            function getImageListSimple() {
+            function getImageList() {
 
                 mainFactory.ImageList().get({}).$promise.then(function(response) {
                     if (response.status == 1) {
-                        $scope.imageListData = response.result.images;
-                        $scope.imageStatus = response.result.status;
-
-                    } else {
-                        toaster.pop({
-                            type: 'error',
-                            title: 'get data failed',
-                            body: 'please retry',
-                            timeout: 3000
+                        angular.forEach($scope.yardstickImage, function(value, key){
+                            if(typeof(response.result.images[key]) != 'undefined'){
+                                $scope.yardstickImage[key] = response.result.images[key];
+                            }
                         });
+                        $scope.imageStatus = response.result.status;
+                    }else{
+                        mainFactory.errorHandler1(response);
                     }
-                }, function(error) {
-                    toaster.pop({
-                        type: 'error',
-                        title: 'get data failed',
-                        body: 'please retry',
-                        timeout: 3000
-                    });
+                }, function(response) {
+                    mainFactory.errorHandler2(response);
                 })
             }
 
index 843f66c..e846804 100644 (file)
@@ -672,7 +672,7 @@ angular.module('yardStickGui2App')
             }
 
             $scope.gotoLog = function gotoLog(task_id) {
-                $state.go('app2.taskLog', { taskId: task_id });
+                $state.go('app.taskLog', { taskId: task_id });
             }
         }
     ]);
index 44fbeb3..7637a9f 100644 (file)
@@ -9,7 +9,7 @@ var Base_URL;
 var Grafana_URL;
 
 angular.module('yardStickGui2App')
-    .factory('mainFactory', ['$resource','$rootScope','$http', '$location',function($resource, $rootScope,$http,$location) {
+    .factory('mainFactory', ['$resource','$rootScope','$http', '$location', 'toaster',function($resource, $rootScope ,$http ,$location, toaster) {
 
         Base_URL = 'http://' + $location.host() + ':' + $location.port();
         Grafana_URL = 'http://' + $location.host();
@@ -86,6 +86,20 @@ angular.module('yardStickGui2App')
                     }
                 })
             },
+            getImage: function(){
+                return $resource(Base_URL + '/api/v2/yardstick/images/:imageId', {imageId: "@imageId"}, {
+                    'get': {
+                        method: 'GET'
+                    }
+                })
+            },
+            deleteImage: function() {
+                return $resource(Base_URL + '/api/v2/yardstick/images/:imageId', { imageId: '@imageId' }, {
+                    'delete': {
+                        method: 'DELETE'
+                    }
+                })
+            },
             uploadImage: function() {
                 return $resource(Base_URL + '/api/v2/yardstick/images', {}, {
                     'post': {
@@ -93,6 +107,13 @@ angular.module('yardStickGui2App')
                     }
                 })
             },
+            uploadImageByUrl: function() {
+                return $resource(Base_URL + '/api/v2/yardstick/images', {}, {
+                    'post': {
+                        method: 'POST'
+                    }
+                })
+            },
             getPodDetail: function() {
                 return $resource(Base_URL + '/api/v2/yardstick/pods/:podId', { podId: "@podId" }, {
                     'get': {
@@ -249,6 +270,22 @@ angular.module('yardStickGui2App')
                         method: 'DELETE'
                     }
                 })
+            },
+            errorHandler1: function(response){
+                toaster.pop({
+                    'type': 'error',
+                    'title': 'error',
+                    'body': response.result,
+                    'showCloseButton': true
+                });
+            },
+            errorHandler2: function(response){
+                toaster.pop({
+                    'type': 'error',
+                    'title': response.status,
+                    'body': response.statusText,
+                    'showCloseButton': true
+                });
             }
 
         };
index 389de83..4c539fc 100644 (file)
                     <table class="table table-striped">
 
                         <tr>
+                            <th>choose</th>
                             <th>name</th>
-                            <th>size</th>
+                            <th>description</th>
                             <th>status</th>
-                            <th>time</th>
                         </tr>
-                        <tr ng-repeat="image in imageListData">
-                            <td>{{image.name}}</td>
-                            <td>{{image.size/1024}} mb</td>
-                            <td>{{image.status}}</td>
-                            <td>{{image.time}}</td>
+                        <tr ng-repeat="(name, value) in yardstickImage">
+                            <td ng-if="selectImageList.indexOf(name) > -1"><img src="images/checkyes.png" style="height:12px;cursor:pointer" ng-click="unselectImage(name)" /></td>
+                            <td ng-if="selectImageList.indexOf(name) == -1"><img src="images/checkno.png" style="height:12px;cursor:pointer" ng-click="selectImage(name)" /></td>
+                            <td>{{name}}</td>
+                            <td>{{value.description}}</td>
+                            <td>{{value.status}}</td>
 
                         </tr>
 
diff --git a/gui/app/views/modal/imageDialog.html b/gui/app/views/modal/imageDialog.html
new file mode 100644 (file)
index 0000000..c568f2a
--- /dev/null
@@ -0,0 +1,19 @@
+<div>
+
+    <h4>Enter Remote Image Url</h4>
+    <input type="text" ng-model="url" />
+
+    <div style="text-align:center;margin-top:20px;">
+        <button class="btn btn-default" ng-disabled=" url==null || url==''" ng-click="uploadCustomImageByUrl(url)">Upload</button>
+    </div>
+
+</div>
+
+
+<style>
+    input {
+        border-radius: 10px;
+        border: 1px solid #eeeeee;
+        width: 100%;
+    }
+</style>
index 99e83ac..d6d7c0c 100644 (file)
@@ -13,7 +13,7 @@
 
             <hr/>
 
-            <button class="btn btn-default" ngf-select="uploadFiles($file, $invalidFiles)" ngf-max-size="5MB">
+            <button class="btn btn-default" ngf-select="uploadFiles($file, $invalidFiles)" ngf-max-size="1024MB">
                                     <div ng-show="!loadingOPENrc">Upload</div>
                                      <img src="images/loading2.gif" width="25" height="25" ng-if="loadingOPENrc" />
             </button>
index 17ccfdb..0c337fe 100644 (file)
@@ -4,56 +4,86 @@
     <div style="display:flex;flex-direction:row;">
         <div style="width:750px;">
 
-            <h3>{{baseElementInfo.name}} -- Image
+            <h3>{{environmentInfo.name}} -- Image
                 <button class="btn btn-default" style="float:right" ng-click="goNext()">Next</button>
             </h3>
             <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
 
-            <hr/>
-            <button class="btn btn-default" ng-click="uploadImage()">
-                 <div ng-if="!showloading">Load Image</div>
-                 <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
-            </button>
-            <i class="fa fa-check" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="imageStatus==1&&ifshowStatus==1">done</i>
-            <i class="fa fa-spinner" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="imageStatus==0&&ifshowStatus==1">loading</i>
-            <i class="fa fa-exclamation-triangle" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: red;" ng-show="imageStatus==2&&ifshowStatus==1">error</i>
-
             <hr>
-            <h4>Current Images</h4>
-
+            <h4>Alternative Images</h4>
             <div>
                 <table class="table table-striped">
 
                     <tr>
                         <th>name</th>
+                        <th>description</th>
                         <th>size</th>
                         <th>status</th>
                         <th>time</th>
+                        <th>action</th>
                     </tr>
-                    <tr ng-repeat="image in imageListData">
+                    <tr ng-repeat="image in yardstickImage">
                         <td>{{image.name}}</td>
-                        <td>{{image.size/1024}} MB</td>
+                        <td>{{image.description}}</td>
+                        <td>{{image.size | number:2}} MB</td>
                         <td>{{image.status}}</td>
                         <td>{{image.time}}</td>
-
+                        <td>
+                            <div class="btn-group" uib-dropdown>
+                                <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle>
+                                    action<span class="caret"></span>
+                                </button>
+                                <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
+                                    <li role="menuitem" ng-show="image.status == 'N/A'"><a ng-click="loadYardstickImage(image.name)">load</a></li>
+                                    <li role="menuitem" ng-show="image.status != 'N/A'"><a ng-click="deleteYardstickImage(image.name)">delete</a></li>
+                                </ul>
+                            </div>
+                        </td>
                     </tr>
-
-
-
                 </table>
             </div>
 
+            <hr>
+            <h4 style="display:inline">Custom Images</h4>
+            <div class="btn-group button-margin" style="float:right;margin-top:-10px;margin-bottom:5px">
+                <button class="btn btn-default" style="width:60px" ngf-select="uploadCustomImage($file, $invalidFiles)" ngf-max-size="2048MB">
+                        <div ng-show="!showloading">Local</div>
+                        <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
+                </button>
+                <button class="btn btn-default" style="width:60px" ng-click="openImageDialog()">Url</button>
+            </div>
+            <div>
+                <table class="table table-striped">
 
-
-
-
-
-
-
+                    <tr>
+                        <th>name</th>
+                        <th>description</th>
+                        <th>size</th>
+                        <th>status</th>
+                        <th>time</th>
+                        <th>action</th>
+                    </tr>
+                    <tr ng-repeat="image in customImage">
+                        <td>{{image.name}}</td>
+                        <td>{{image.description}}</td>
+                        <td>{{image.size | number:2}} MB</td>
+                        <td>{{image.status}}</td>
+                        <td>{{image.time}}</td>
+                        <td>
+                            <div class="btn-group" uib-dropdown>
+                                <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle>
+                                    action<span class="caret"></span>
+                                </button>
+                                <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
+                                    <li role="menuitem" ><a ng-click="deleteCustomImage(image.id)">delete</a></li>
+                                </ul>
+                            </div>
+                        </td>
+                    </tr>
+                </table>
+            </div>
 
         </div>
-
-
     </div>
 
 </div>
index fe394fd..ec683e9 100644 (file)
@@ -59,6 +59,7 @@ if not SERVER_IP:
 
 # dir
 CONF_DIR = get_param('dir.conf', '/etc/yardstick')
+IMAGE_DIR = get_param('dir.images', '/home/opnfv/images/')
 REPOS_DIR = get_param('dir.repos', '/home/opnfv/repos/yardstick')
 RELENG_DIR = get_param('dir.releng', '/home/opnfv/repos/releng')
 LOG_DIR = get_param('dir.log', '/tmp/yardstick/')