changeset 196:a9b83aee75b0

Extract system-cpu into component Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/024926.html
author Andrew Azores <aazores@redhat.com>
date Tue, 12 Sep 2017 12:20:37 -0400
parents ce6984e324a1
children ed977dff0ba2
files src/app/components/system-info/en.locale.yaml src/app/components/system-info/system-cpu.controller.js src/app/components/system-info/system-cpu.controller.spec.js src/app/components/system-info/system-cpu/en.locale.yaml src/app/components/system-info/system-cpu/system-cpu.component.js src/app/components/system-info/system-cpu/system-cpu.controller.js src/app/components/system-info/system-cpu/system-cpu.controller.spec.js src/app/components/system-info/system-cpu/system-cpu.html src/app/components/system-info/system-cpu/system-cpu.service.js src/app/components/system-info/system-cpu/system-cpu.service.spec.js src/app/components/system-info/system-info.html src/app/components/system-info/system-info.module.js src/app/components/system-info/system-info.service.js src/app/components/system-info/system-info.service.spec.js
diffstat 14 files changed, 439 insertions(+), 299 deletions(-) [+]
line wrap: on
line diff
--- a/src/app/components/system-info/en.locale.yaml	Tue Sep 12 12:53:42 2017 -0400
+++ b/src/app/components/system-info/en.locale.yaml	Tue Sep 12 12:20:37 2017 -0400
@@ -31,9 +31,6 @@
     SECONDS: '{SECONDS, plural, =0{0 Seconds} one{1 Second} other{# Seconds}}{DEFAULT, select, true{ (Default)} other{}}'
     MINUTES: '{MINUTES, plural, =0{0 Minutes} one{1 Minute} other{# Minutes}}{DEFAULT, select, true{ (Default)} other{}}'
 
-  systemCpu:
-    CHART_LABEL: CPU Usage
-
   systemMemory:
     DONUT_CHART_LABEL: Memory Usage
     LINE_CHART_LABEL: Memory
--- a/src/app/components/system-info/system-cpu.controller.js	Tue Sep 12 12:53:42 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-import 'c3';
-import _ from 'lodash';
-import filters from 'shared/filters/filters.module.js';
-import service from './system-info.service.js';
-
-class SystemCpuController {
-  constructor (systemInfoService, $scope, $interval) {
-    this.svc = systemInfoService;
-    this.scope = $scope;
-
-    this.data = {
-      used: 0,
-      total: 0
-    };
-
-    this.config = {
-      chartId: 'cpuChart',
-      units: '%'
-    };
-
-    this.refresh = $interval(() => this.update(), 2000);
-
-    $scope.$on('$destroy', () => {
-      if (angular.isDefined(this.refresh)) {
-        $interval.cancel(this.refresh);
-      }
-    });
-
-    this.update();
-  }
-
-  update () {
-    this.svc.getCpuInfo(this.scope.systemId).then(resp => {
-      let cpuInfo = resp.data.response[0];
-      this.data = {
-        used: _.floor(_.mean(cpuInfo.perProcessorUsage)),
-        total: 100
-      };
-    });
-  }
-
-  multichartFn () {
-    return new Promise(resolve =>
-      this.svc.getCpuInfo(this.scope.systemId).then(resp =>
-        resolve(_.floor(_.mean(resp.data.response[0].perProcessorUsage)))
-      )
-    );
-  }
-}
-
-export default angular
-  .module('systemCpu.controller', [
-    'patternfly',
-    'patternfly.charts',
-    filters,
-    service
-  ])
-  .controller('SystemCpuController', SystemCpuController)
-  .name;
--- a/src/app/components/system-info/system-cpu.controller.spec.js	Tue Sep 12 12:53:42 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-/**
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * Thermostat is distributed under the GNU General Public License,
- * version 2 or any later version (with a special exception described
- * below, commonly known as the "Classpath Exception").
- *
- * A copy of GNU General Public License (GPL) is included in this
- * distribution, in the file COPYING.
- *
- * Linking Thermostat code with other modules is making a combined work
- * based on Thermostat.  Thus, the terms and conditions of the GPL
- * cover the whole combination.
- *
- * As a special exception, the copyright holders of Thermostat give you
- * permission to link this code with independent modules to produce an
- * executable, regardless of the license terms of these independent
- * modules, and to copy and distribute the resulting executable under
- * terms of your choice, provided that you also meet, for each linked
- * independent module, the terms and conditions of the license of that
- * module.  An independent module is a module which is not derived from
- * or based on Thermostat code.  If you modify Thermostat, you may
- * extend this exception to your version of the software, but you are
- * not obligated to do so.  If you do not wish to do so, delete this
- * exception statement from your version.
- */
-
-describe('SystemCpuController', () => {
-
-  beforeEach(angular.mock.module('systemCpu.controller'));
-
-  let service, scope, interval, controller;
-  beforeEach(inject($controller => {
-    'ngInject';
-
-    let systemPromise = sinon.spy();
-    let cpuPromise = sinon.spy();
-    let memoryPromise = sinon.spy();
-    service = {
-      systemPromise: systemPromise,
-      cpuPromise: cpuPromise,
-      memoryPromise: memoryPromise,
-      getSystemInfo: sinon.stub().returns({ then: systemPromise }),
-      getCpuInfo: sinon.stub().returns({ then: cpuPromise }),
-      getMemoryInfo: sinon.stub().returns({ then: memoryPromise })
-    };
-
-    scope = {
-      $on: sinon.spy(),
-      systemId: 'foo-systemId'
-    };
-
-    interval = sinon.stub().returns('interval-sentinel');
-    interval.cancel = sinon.stub().returns(interval.sentinel);
-    controller = $controller('SystemCpuController', {
-      systemInfoService: service,
-      $scope: scope,
-      $interval: interval
-    });
-  }));
-
-  it('should exist', () => {
-    should.exist(controller);
-    should.exist(service);
-  });
-
-  it('should set an initial data object', () => {
-    controller.should.have.ownProperty('data');
-    controller.data.should.deepEqual({
-      used: 0,
-      total: 0
-    });
-  });
-
-  it('should set an initial config object', () => {
-    controller.should.have.ownProperty('config');
-    controller.config.should.deepEqual({
-      chartId: 'cpuChart',
-      units: '%'
-    });
-  });
-
-  it('should have a refresh property', () => {
-    controller.should.have.ownProperty('refresh');
-    controller.refresh.should.equal('interval-sentinel');
-  });
-
-  describe('interval', () => {
-    it('should call service.getCpuInfo', () => {
-      let func = interval.args[0][0];
-      func.should.be.a.Function();
-      service.getCpuInfo.should.be.calledOnce(); // on initial load
-      func();
-      service.getCpuInfo.should.be.calledTwice();
-      service.getCpuInfo.should.be.calledWith(scope.systemId);
-    });
-
-    it('should be every 2 seconds', () => {
-      interval.should.be.calledWith(sinon.match.func, sinon.match(2000));
-    });
-
-    describe('action', () => {
-      it('should resolve data', () => {
-        interval.args[0][0]();
-        service.cpuPromise.should.be.called();
-        let func = service.cpuPromise.args[0][0];
-        func.should.be.a.Function();
-        let mockData = {
-          data: {
-            response: [{
-              perProcessorUsage: [80]
-            }]
-          }
-        };
-        func(mockData);
-        controller.data.should.deepEqual({
-          used: mockData.data.response[0].perProcessorUsage[0],
-          total: 100
-        });
-      });
-    });
-  });
-
-  describe('on destroy', () => {
-    it('should set an ondestroy handler', () => {
-      scope.$on.should.be.calledWith('$destroy', sinon.match.func);
-    });
-
-    it('should cancel refresh', () => {
-      let func = scope.$on.args[0][1];
-      func();
-      interval.cancel.should.be.calledWith('interval-sentinel');
-    });
-
-    it('should do nothing if refresh undefined', () => {
-      controller.refresh = undefined;
-      let func = scope.$on.args[0][1];
-      func();
-      interval.cancel.should.not.be.called();
-    });
-  });
-
-  describe('multichartFn', () => {
-    it('should return a promise', () => {
-      let res = controller.multichartFn();
-      res.should.be.a.Promise();
-    });
-
-    it('should resolve system-cpu stat', done => {
-      service.cpuPromise.should.be.calledOnce();
-      let res = controller.multichartFn();
-      res.then(v => {
-        v.should.equal(90);
-        done();
-      });
-      service.cpuPromise.should.be.calledTwice();
-      let prom = service.cpuPromise.secondCall.args[0];
-      prom({
-        data: {
-          response: [
-            {
-              perProcessorUsage: [90]
-            }
-          ]
-        }
-      });
-    });
-  });
-
-});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/system-info/system-cpu/en.locale.yaml	Tue Sep 12 12:20:37 2017 -0400
@@ -0,0 +1,2 @@
+  systemCpu:
+    CHART_LABEL: CPU Usage
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/system-info/system-cpu/system-cpu.component.js	Tue Sep 12 12:20:37 2017 -0400
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import controller from './system-cpu.controller.js';
+import service from './system-cpu.service.js';
+
+export default angular
+  .module('systemCpu.component', [
+    controller,
+    service
+  ])
+  .component('systemCpu', {
+    bindings: {
+      systemId: '<'
+    },
+    controller: 'SystemCpuController',
+    template: require('./system-cpu.html')
+  })
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/system-info/system-cpu/system-cpu.controller.js	Tue Sep 12 12:20:37 2017 -0400
@@ -0,0 +1,95 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import 'c3';
+import _ from 'lodash';
+import service from './system-cpu.service.js';
+
+class SystemCpuController {
+  constructor (systemCpuService, $interval) {
+    this._svc = systemCpuService;
+    this._interval = $interval;
+
+    this.data = {
+      used: 0,
+      total: 100
+    };
+
+    this.config = {
+      chartId: 'cpuChart',
+      units: '%'
+    };
+  }
+
+  $onInit () {
+    this._start();
+  }
+
+  _start () {
+    this._stop();
+    this._update();
+    this._refresh = this._interval(() => this._update(), 2000);
+  }
+
+  _stop () {
+    if (angular.isDefined(this._refresh)) {
+      this._interval.cancel(this._refresh);
+      delete this._refresh;
+    }
+  }
+
+  $onDestroy () {
+    this._stop();
+  }
+
+  _update () {
+    this._svc.getCpuInfo(this.systemId).then(resp => {
+      let cpuInfo = resp.data.response[0];
+      this.data = {
+        used: _.floor(_.mean(cpuInfo.perProcessorUsage)),
+        total: 100
+      };
+    });
+  }
+
+  multichartFn () {
+    return new Promise(resolve =>
+      this._svc.getCpuInfo(this.systemId).then(resp =>
+        resolve(_.floor(_.mean(resp.data.response[0].perProcessorUsage)))
+      )
+    );
+  }
+}
+
+export default angular
+  .module('systemCpu.controller', [
+    'patternfly',
+    'patternfly.charts',
+    service
+  ])
+  .controller('SystemCpuController', SystemCpuController)
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/system-info/system-cpu/system-cpu.controller.spec.js	Tue Sep 12 12:20:37 2017 -0400
@@ -0,0 +1,156 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import controllerModule from './system-cpu.controller.js';
+
+describe('SystemCpuController', () => {
+
+  beforeEach(angular.mock.module(controllerModule));
+
+  let service, interval, ctrl;
+  beforeEach(inject($controller => {
+    'ngInject';
+
+    let cpuPromise = sinon.spy();
+    service = {
+      cpuPromise: cpuPromise,
+      getCpuInfo: sinon.stub().returns({ then: cpuPromise }),
+    };
+
+    interval = sinon.stub().returns('interval-sentinel');
+    interval.cancel = sinon.stub().returns(interval.sentinel);
+    ctrl = $controller('SystemCpuController', {
+      systemCpuService: service,
+      $interval: interval
+    });
+    ctrl.systemId = 'foo-systemId';
+    ctrl.$onInit();
+  }));
+
+  it('should exist', () => {
+    should.exist(ctrl);
+    should.exist(service);
+  });
+
+  it('should set an initial data object', () => {
+    ctrl.should.have.ownProperty('data');
+    ctrl.data.should.deepEqual({
+      used: 0,
+      total: 100
+    });
+  });
+
+  it('should set an initial config object', () => {
+    ctrl.should.have.ownProperty('config');
+    ctrl.config.should.deepEqual({
+      chartId: 'cpuChart',
+      units: '%'
+    });
+  });
+
+  it('should have a refresh property', () => {
+    ctrl.should.have.ownProperty('_refresh');
+    ctrl._refresh.should.equal('interval-sentinel');
+  });
+
+  describe('interval', () => {
+    it('should call service.getCpuInfo', () => {
+      service.getCpuInfo.should.be.calledOnce();
+      let func = interval.args[0][0];
+      func.should.be.a.Function();
+      func();
+      service.getCpuInfo.should.be.calledTwice();
+      service.getCpuInfo.should.be.calledWith(ctrl.systemId);
+    });
+
+    it('should be every 2 seconds', () => {
+      interval.should.be.calledWith(sinon.match.func, sinon.match(2000));
+    });
+
+    describe('action', () => {
+      it('should resolve data', () => {
+        interval.args[0][0]();
+        service.cpuPromise.should.be.called();
+        let func = service.cpuPromise.args[0][0];
+        func.should.be.a.Function();
+        let mockData = {
+          data: {
+            response: [{
+              perProcessorUsage: [80]
+            }]
+          }
+        };
+        func(mockData);
+        ctrl.data.should.deepEqual({
+          used: mockData.data.response[0].perProcessorUsage[0],
+          total: 100
+        });
+      });
+    });
+  });
+
+  describe('on destroy', () => {
+    it('should cancel refresh', () => {
+      ctrl.$onDestroy();
+      interval.cancel.should.be.calledWith('interval-sentinel');
+    });
+
+    it('should do nothing if refresh undefined', () => {
+      delete ctrl._refresh;
+      ctrl.$onDestroy();
+      interval.cancel.should.not.be.called();
+    });
+  });
+
+  describe('multichartFn', () => {
+    it('should return a promise', () => {
+      let res = ctrl.multichartFn();
+      res.should.be.a.Promise();
+    });
+
+    it('should resolve system-cpu stat', done => {
+      service.cpuPromise.should.be.calledOnce();
+      let res = ctrl.multichartFn();
+      res.then(v => {
+        v.should.equal(90);
+        done();
+      });
+      service.cpuPromise.should.be.calledTwice();
+      let prom = service.cpuPromise.args[1][0];
+      prom({
+        data: {
+          response: [
+            {
+              perProcessorUsage: [90]
+            }
+          ]
+        }
+      });
+    });
+  });
+
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/system-info/system-cpu/system-cpu.html	Tue Sep 12 12:20:37 2017 -0400
@@ -0,0 +1,11 @@
+<div class="col-xs-12 col-sm-6 col-md-6">
+  <div class="card-pf card-pf-view">
+    <div class="card-pf-heading">
+      <label class="card-pf-title" translate>systemCpu.CHART_LABEL</label>
+      <mc-add class="pull-right" svc-name="{{$ctrl.systemId}}-cpu" get-fn="$ctrl.multichartFn()"></mc-add>
+    </div>
+    <div class="card-pf-body">
+      <pf-donut-pct-chart id="cpuChart" config="$ctrl.config" data="$ctrl.data"></pf-donut-pct-chart>
+    </div>
+  </div>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/system-info/system-cpu/system-cpu.service.js	Tue Sep 12 12:20:37 2017 -0400
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import config from 'shared/config/config.module.js';
+import urlJoin from 'url-join';
+
+class SystemCpuService {
+  constructor ($q, $http, gatewayUrl) {
+    'ngInject';
+    this.q = $q;
+    this.http = $http;
+    this.gatewayUrl = gatewayUrl;
+  }
+
+  getCpuInfo (systemId) {
+    return this.http.get(urlJoin(this.gatewayUrl, 'system-cpu', '0.0.1', 'systems', systemId), {
+      params: {
+        sort: '-timeStamp',
+        limit: 1
+      }
+    });
+  }
+}
+
+export default angular
+  .module('systemCpu.service', [config])
+  .service('systemCpuService', SystemCpuService)
+  .name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/app/components/system-info/system-cpu/system-cpu.service.spec.js	Tue Sep 12 12:20:37 2017 -0400
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * Thermostat is distributed under the GNU General Public License,
+ * version 2 or any later version (with a special exception described
+ * below, commonly known as the "Classpath Exception").
+ *
+ * A copy of GNU General Public License (GPL) is included in this
+ * distribution, in the file COPYING.
+ *
+ * Linking Thermostat code with other modules is making a combined work
+ * based on Thermostat.  Thus, the terms and conditions of the GPL
+ * cover the whole combination.
+ *
+ * As a special exception, the copyright holders of Thermostat give you
+ * permission to link this code with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module.  An independent module is a module which is not derived from
+ * or based on Thermostat code.  If you modify Thermostat, you may
+ * extend this exception to your version of the software, but you are
+ * not obligated to do so.  If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+import configModule from 'shared/config/config.module.js';
+import serviceModule from './system-cpu.service.js';
+
+describe('SystemCpuService', () => {
+
+  beforeEach(() => {
+    angular.mock.module(configModule, $provide => {
+      'ngInject';
+      $provide.constant('gatewayUrl', 'http://example.com:1234');
+    });
+
+    angular.mock.module(serviceModule);
+  });
+
+  let httpBackend, scope, svc;
+  beforeEach(inject(($httpBackend, $rootScope, systemCpuService) => {
+    'ngInject';
+    httpBackend = $httpBackend;
+
+    scope = $rootScope;
+    svc = systemCpuService;
+  }));
+
+  afterEach(() => {
+    httpBackend.verifyNoOutstandingExpectation();
+    httpBackend.verifyNoOutstandingRequest();
+  });
+
+  it('should exist', () => {
+    should.exist(svc);
+  });
+
+  describe('getCpuInfo(systemId)', () => {
+    it('should resolve mock data', done => {
+      let expected = {
+        percent: 80
+      };
+      httpBackend.when('GET', 'http://example.com:1234/system-cpu/0.0.1/systems/foo-systemId?limit=1&sort=-timeStamp')
+        .respond(expected);
+      svc.getCpuInfo('foo-systemId').then(res => {
+        res.data.should.deepEqual(expected);
+        done();
+      });
+      httpBackend.expectGET('http://example.com:1234/system-cpu/0.0.1/systems/foo-systemId?limit=1&sort=-timeStamp');
+      httpBackend.flush();
+      scope.$apply();
+    });
+  });
+
+});
--- a/src/app/components/system-info/system-info.html	Tue Sep 12 12:53:42 2017 -0400
+++ b/src/app/components/system-info/system-info.html	Tue Sep 12 12:20:37 2017 -0400
@@ -83,18 +83,7 @@
 
     <div class="row row-cards-pf">
       <div class="container container-cards-pf">
-        <!-- System-CPU Donut Chart -->
-        <div class="col-xs-12 col-sm-6 col-md-6" ng-controller="SystemCpuController as ctrl">
-          <div class="card-pf card-pf-view">
-            <div class="card-pf-heading">
-              <label class="card-pf-title" translate>systemInfo.systemCpu.CHART_LABEL</label>
-              <mc-add class="pull-right" svc-name="{{systemId}}-cpu" get-fn="ctrl.multichartFn()"></mc-add>
-            </div>
-            <div class="card-pf-body">
-              <pf-donut-pct-chart id="cpuChart" config="ctrl.config" data="ctrl.data"></pf-donut-pct-chart>
-            </div>
-          </div>
-        </div>
+        <system-cpu system-id="ctrl.systemId"></system-cpu>
         <div class="system-memory-charts" ng-controller="SystemMemoryController as ctrl">
           <!-- System-Memory Donut Chart -->
           <div class="col-xs-12 col-sm-6 col-md-6">
--- a/src/app/components/system-info/system-info.module.js	Tue Sep 12 12:53:42 2017 -0400
+++ b/src/app/components/system-info/system-info.module.js	Tue Sep 12 12:20:37 2017 -0400
@@ -26,7 +26,7 @@
  */
 
 import SystemInfocontroller from './system-info.controller.js';
-import SystemCpuController from './system-cpu.controller.js';
+import systemCpu from './system-cpu/system-cpu.component.js';
 import SystemMemoryController from './system-memory.controller.js';
 import service from './system-info.service.js';
 import components from 'shared/components/components.module.js';
@@ -34,7 +34,7 @@
 export default angular
   .module('systemInfo', [
     SystemInfocontroller,
-    SystemCpuController,
+    systemCpu,
     SystemMemoryController,
     service,
     components
--- a/src/app/components/system-info/system-info.service.js	Tue Sep 12 12:53:42 2017 -0400
+++ b/src/app/components/system-info/system-info.service.js	Tue Sep 12 12:20:37 2017 -0400
@@ -45,15 +45,6 @@
     });
   }
 
-  getCpuInfo (systemId) {
-    return this.http.get(urlJoin(this.gatewayUrl, 'system-cpu', '0.0.1', 'systems', systemId), {
-      params: {
-        sort: '-timeStamp',
-        limit: 1
-      }
-    });
-  }
-
   getMemoryInfo (systemId) {
     return this.http.get(urlJoin(this.gatewayUrl, 'system-memory', '0.0.1', 'systems', systemId), {
       params: {
--- a/src/app/components/system-info/system-info.service.spec.js	Tue Sep 12 12:53:42 2017 -0400
+++ b/src/app/components/system-info/system-info.service.spec.js	Tue Sep 12 12:20:37 2017 -0400
@@ -72,23 +72,6 @@
     });
   });
 
-  describe('getCpuInfo(systemId)', () => {
-    it('should resolve mock data', done => {
-      let expected = {
-        percent: 80
-      };
-      httpBackend.when('GET', 'http://example.com:1234/system-cpu/0.0.1/systems/foo-systemId?limit=1&sort=-timeStamp')
-        .respond(expected);
-      svc.getCpuInfo('foo-systemId').then(res => {
-        res.data.should.deepEqual(expected);
-        done();
-      });
-      httpBackend.expectGET('http://example.com:1234/system-cpu/0.0.1/systems/foo-systemId?limit=1&sort=-timeStamp');
-      httpBackend.flush();
-      scope.$apply();
-    });
-  });
-
   describe('getMemoryInfo(systemId)', () => {
     it('should resolve mock data', done => {
       let expected = {