From 036d42aef7b93bada6b3de5596b8740b388e17bf Mon Sep 17 00:00:00 2001 From: Westly Wright Date: Mon, 1 Aug 2016 21:27:30 -0700 Subject: [PATCH] Add new snapshot timeline (#800) --- app/application/controller.js | 1 + app/application/template.hbs | 2 +- app/authenticated/dummy-dev/controller.js | 4 + app/authenticated/dummy-dev/route.js | 7 + app/authenticated/dummy-dev/template.hbs | 4 + app/authenticated/route.js | 16 +- .../device-permissions/component.js | 75 ++++-- .../device-permissions/template.hbs | 14 +- app/components/form-command/component.js | 21 +- app/components/form-command/template.hbs | 143 ++++++----- app/components/form-healthcheck/template.hbs | 156 +++++++----- app/components/form-key-value/component.js | 24 +- app/components/form-key-value/template.hbs | 40 ++- app/components/form-networking/component.js | 1 + app/components/form-networking/template.hbs | 210 +++++++++++----- app/components/form-scheduling/component.js | 4 + app/components/form-scheduling/template.hbs | 109 +++++---- app/components/form-security/component.js | 4 + app/components/form-security/template.hbs | 227 ++++++++++++------ app/components/info-multi-stats/component.js | 1 + app/components/info-multi-stats/template.hbs | 8 +- app/components/input-or-display/component.js | 9 + app/components/input-or-display/template.hbs | 9 + app/components/new-container/template.hbs | 11 +- .../scheduling-rule-row/component.js | 6 + .../scheduling-rule-row/template.hbs | 208 +++++++++------- app/components/snapshot-section/template.hbs | 19 -- app/components/snapshot-timeline/component.js | 82 +++++++ app/components/snapshot-timeline/template.hbs | 13 + .../storagepool-section/template.hbs | 3 +- .../tooltip-action-menu/component.js | 9 +- .../tooltip-action-menu/template.hbs | 5 - .../backups => container/commands}/route.js | 2 +- app/container/commands/template.hbs | 7 + app/container/healthcheck/route.js | 7 + app/container/healthcheck/template.hbs | 5 + app/container/index/route.js | 2 +- app/container/networking/route.js | 12 + app/container/networking/template.hbs | 8 + app/container/scheduling/route.js | 12 + app/container/scheduling/template.hbs | 8 + app/container/security/route.js | 7 + app/container/security/template.hbs | 3 + app/container/template.hbs | 96 +++++--- app/container/volumes/template.hbs | 32 +-- app/mixins/cattle-transitioning-resource.js | 2 + app/models/mount.js | 40 ++- app/models/snapshot.js | 45 +--- app/models/volume.js | 19 -- app/router.js | 7 +- app/storagepools/backups/controller.js | 14 -- app/storagepools/backups/template.hbs | 36 --- app/storagepools/route.js | 2 - app/storagepools/template.hbs | 4 - app/styles/app-dark.scss | 1 + app/styles/app-light.scss | 1 + app/styles/components/_timeline.scss | 65 +++++ app/styles/layout/_layout.scss | 3 + app/styles/layout/_md-screen.scss | 17 +- app/styles/layout/_sm-screen.scss | 141 +++++------ app/styles/pages/_loading.scss | 11 +- app/templates/tooltip-action-menu.hbs | 5 + app/templates/tooltip-snapshot-timeline.hbs | 9 + app/utils/constants.js | 3 +- tests/unit/storagepools/backups/route-test.js | 11 - translations/en-us.yaml | 30 ++- vendor/icons | 2 +- 67 files changed, 1368 insertions(+), 746 deletions(-) create mode 100644 app/authenticated/dummy-dev/controller.js create mode 100644 app/authenticated/dummy-dev/route.js create mode 100644 app/authenticated/dummy-dev/template.hbs create mode 100644 app/components/input-or-display/component.js create mode 100644 app/components/input-or-display/template.hbs create mode 100644 app/components/snapshot-timeline/component.js create mode 100644 app/components/snapshot-timeline/template.hbs delete mode 100644 app/components/tooltip-action-menu/template.hbs rename app/{storagepools/backups => container/commands}/route.js (62%) create mode 100644 app/container/commands/template.hbs create mode 100644 app/container/healthcheck/route.js create mode 100644 app/container/healthcheck/template.hbs create mode 100644 app/container/networking/route.js create mode 100644 app/container/networking/template.hbs create mode 100644 app/container/scheduling/route.js create mode 100644 app/container/scheduling/template.hbs create mode 100644 app/container/security/route.js create mode 100644 app/container/security/template.hbs delete mode 100644 app/storagepools/backups/controller.js delete mode 100644 app/storagepools/backups/template.hbs create mode 100644 app/styles/components/_timeline.scss create mode 100644 app/templates/tooltip-action-menu.hbs create mode 100644 app/templates/tooltip-snapshot-timeline.hbs delete mode 100644 tests/unit/storagepools/backups/route-test.js diff --git a/app/application/controller.js b/app/application/controller.js index cfb974235..9412ab666 100644 --- a/app/application/controller.js +++ b/app/application/controller.js @@ -10,6 +10,7 @@ export default Ember.Controller.extend({ tooltipService : Ember.inject.service('tooltip'), tooltip : Ember.computed.alias('tooltipService.tooltipOpts.type'), + tooltipTemplate : Ember.computed.alias('tooltipService.tooltipOpts.template'), error : null, error_description : null, diff --git a/app/application/template.hbs b/app/application/template.hbs index ffcf357b1..9143f6d8c 100644 --- a/app/application/template.hbs +++ b/app/application/template.hbs @@ -53,4 +53,4 @@ -{{component tooltip class="container-tooltip" id="tooltip-base" role="tooltip" aria-hidden="false"}} +{{component tooltip tooltipTemplate=tooltipTemplate class="container-tooltip" id="tooltip-base" role="tooltip" aria-hidden="false"}} diff --git a/app/authenticated/dummy-dev/controller.js b/app/authenticated/dummy-dev/controller.js new file mode 100644 index 000000000..55ff9aa58 --- /dev/null +++ b/app/authenticated/dummy-dev/controller.js @@ -0,0 +1,4 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ +}); diff --git a/app/authenticated/dummy-dev/route.js b/app/authenticated/dummy-dev/route.js new file mode 100644 index 000000000..c24ce3529 --- /dev/null +++ b/app/authenticated/dummy-dev/route.js @@ -0,0 +1,7 @@ +import Ember from 'ember'; + +export default Ember.Route.extend({ + model: function() { + return [ ]; + }, +}); diff --git a/app/authenticated/dummy-dev/template.hbs b/app/authenticated/dummy-dev/template.hbs new file mode 100644 index 000000000..3b64196ab --- /dev/null +++ b/app/authenticated/dummy-dev/template.hbs @@ -0,0 +1,4 @@ +
+
+
+
diff --git a/app/authenticated/route.js b/app/authenticated/route.js index 5a4737a17..7cc4f33df 100644 --- a/app/authenticated/route.js +++ b/app/authenticated/route.js @@ -42,6 +42,8 @@ export default Ember.Route.extend(Subscribe, { let isAdmin = (type === C.USER.TYPE_ADMIN) || !this.get('access.enabled'); this.set('access.admin', isAdmin); + var store = this.get('store'); + return Ember.RSVP.hash({ schemas: this.loadUserSchemas(), projects: this.loadProjects(), @@ -54,6 +56,10 @@ export default Ember.Route.extend(Subscribe, { projectId = transition.params['authenticated.project'].project_id; } + if (this.get(`prefs.${C.PREFS.I_HATE_SPINNERS}`)) { + Ember.$('BODY').addClass('i-hate-spinners'); + } + // Make sure a valid project is selected return this.get('projects').selectDefault(projectId).then((project) => { // Load stuff that is needed to draw the header @@ -62,10 +68,12 @@ export default Ember.Route.extend(Subscribe, { return Ember.RSVP.hash({ language: this.get('language').setLanguage(), orchestrationState: this.get('projects').updateOrchestrationState(), - hosts: this.get('store').findAllUnremoved('host'), - machines: this.get('store').findAllUnremoved('machine'), - stacks: this.get('store').findAllUnremoved('environment'), - mounts: this.get('store').findAllUnremoved('mount'), // the container model needs access + hosts: store.findAllUnremoved('host'), + machines: store.findAllUnremoved('machine'), + stacks: store.findAllUnremoved('environment'), + mounts: store.findAllUnremoved('mount'), // the container model needs access + volumes: store.findAllUnremoved('volume'), + snapshots: store.findAllUnremoved('snapshot'), }).then((moreHash) => { Ember.merge(hash, moreHash); diff --git a/app/components/device-permissions/component.js b/app/components/device-permissions/component.js index 0caff01c1..d2fa30e71 100644 --- a/app/components/device-permissions/component.js +++ b/app/components/device-permissions/component.js @@ -1,17 +1,20 @@ import Ember from 'ember'; +const { computed, get/*, set*/ } = Ember; + export default Ember.Component.extend({ - intl: Ember.inject.service(), + intl: Ember.inject.service(), - rSelected: false, - wSelected: false, - mSelected: false, + rSelected: false, + wSelected: false, + mSelected: false, + editing: false, - selection: null, + selection: null, init: function() { this._super(); - var sel = this.get('initialSelection'); + var sel = get(this, 'initialSelection'); this.setProperties({ rSelected: sel.indexOf('r') >= 0, wSelected: sel.indexOf('w') >= 0, @@ -27,28 +30,56 @@ export default Ember.Component.extend({ }, didInsertElement: function() { - var moreClass = this.get('buttonClass')||''; - var opts = { - buttonClass: 'btn btn-default' + (moreClass ? ' '+moreClass : ''), - numberDisplayed: 2, - nonSelectedText: this.get('intl').t('devicePermissions.none'), - allSelectedText: this.get('intl').t('devicePermissions.all'), + if (get(this, 'editing')) { + var moreClass = get(this, 'buttonClass')||''; + var opts = { + buttonClass: 'btn btn-default' + (moreClass ? ' '+moreClass : ''), + numberDisplayed: 2, + nonSelectedText: get(this, 'intl').t('devicePermissions.none'), + allSelectedText: get(this, 'intl').t('devicePermissions.all'), - templates: { - li: '
  • ', - }, - }; + templates: { + li: '
  • ', + }, + }; - this.$('SELECT').multiselect(opts); + this.$('SELECT').multiselect(opts); + } }, + humanReadableList: computed('initialSelection', function() { + let permissions = get(this, 'initialSelection').split(''); + let out = []; + + permissions.forEach((perm) => { + switch (perm) { + case 'r': + out.push(get(this, 'intl').tHtml('devicePermissions.read')); + break; + case 'w': + out.push(get(this, 'intl').tHtml('devicePermissions.write')); + break; + case 'm': + out.push(get(this, 'intl').tHtml('devicePermissions.mknod')); + break; + default: + break; + } + }); + + return out.join('/'); + + }), + rebuild: function() { Ember.run.next(() => { - this.$('SELECT').multiselect('setOptions', { - nonSelectedText: this.get('intl').t('devicePermissions.none'), - allSelectedText: this.get('intl').t('devicePermissions.all'), - }); - this.$('SELECT').multiselect('rebuild'); + if (get(this, 'editing')) { + this.$('SELECT').multiselect('setOptions', { + nonSelectedText: get(this, 'intl').t('devicePermissions.none'), + allSelectedText: get(this, 'intl').t('devicePermissions.all'), + }); + this.$('SELECT').multiselect('rebuild'); + } }); }.observes('intl._locale'), }); diff --git a/app/components/device-permissions/template.hbs b/app/components/device-permissions/template.hbs index f81a7d377..84393a67e 100644 --- a/app/components/device-permissions/template.hbs +++ b/app/components/device-permissions/template.hbs @@ -1,5 +1,9 @@ - +{{#if editing}} + +{{else}} + {{humanReadableList}} +{{/if}} diff --git a/app/components/form-command/component.js b/app/components/form-command/component.js index 54ebc2bd3..3bc95204f 100644 --- a/app/components/form-command/component.js +++ b/app/components/form-command/component.js @@ -7,8 +7,10 @@ export default Ember.Component.extend(ManageLabels, { instance: null, errors: null, isService: null, + editing: true, tagName: '', + intl: Ember.inject.service(), init() { this._super(...arguments); @@ -30,25 +32,32 @@ export default Ember.Component.extend(ManageLabels, { var instance = this.get('instance'); var tty = instance.get('tty'); var stdin = instance.get('stdinOpen'); - var out = 'both'; + var out = { + type: 'both', + name: this.get('intl').tHtml('formCommand.console.both'), + }; if ( tty !== undefined || stdin !== undefined ) { if ( tty && stdin ) { - out = 'both'; + out.type = 'both'; + out.name = this.get('intl').tHtml('formCommand.console.both'); } else if ( tty ) { - out = 'terminal'; + out.type = 'terminal'; + out.name = this.get('intl').tHtml('formCommand.console.terminal'); } else if ( stdin ) { - out = 'interactive'; + out.type = 'interactive'; + out.name = this.get('intl').tHtml('formCommand.console.interactive'); } else { - out = 'none'; + out.type = 'none'; + out.name = this.get('intl').tHtml('formCommand.console.none'); } } @@ -59,7 +68,7 @@ export default Ember.Component.extend(ManageLabels, { terminalDidChange: function() { var val = this.get('terminal'); var stdinOpen = ( val === 'interactive' || val === 'both' ); - var tty = (val === 'terminal' || val === 'both'); + var tty = (val.type === 'terminal' || val.type === 'both'); this.set('instance.tty', tty); this.set('instance.stdinOpen', stdinOpen); }.observes('terminal'), diff --git a/app/components/form-command/template.hbs b/app/components/form-command/template.hbs index f01238492..829fd9bff 100644 --- a/app/components/form-command/template.hbs +++ b/app/components/form-command/template.hbs @@ -4,7 +4,9 @@
    - {{input-command class="form-control" type="text" changed=(action (mut instance.command)) initialValue=instance.command placeholder=(t 'formCommand.command.placeholder')}} + {{#input-or-display editable=editing value=instance.command}} + {{input-command class="form-control" type="text" changed=(action (mut instance.command)) initialValue=instance.command placeholder=(t 'formCommand.command.placeholder')}} + {{/input-or-display}}
    @@ -12,7 +14,9 @@
    - {{input-command class="form-control" type="text" changed=(action (mut instance.entryPoint)) initialValue=instance.entryPoint placeholder=(t 'formCommand.entryPoint.placeholder')}} + {{#input-or-display editable=editing value=instance.entryPoint}} + {{input-command class="form-control" type="text" changed=(action (mut instance.entryPoint)) initialValue=instance.entryPoint placeholder=(t 'formCommand.entryPoint.placeholder')}} + {{/input-or-display}}
    @@ -20,13 +24,17 @@
    - {{input type="text" value=instance.workingDir classNames="form-control" placeholder=(t 'formCommand.workingDir.placeholder')}} + {{#input-or-display editable=editing value=instance.workingDir}} + {{input type="text" value=instance.workingDir classNames="form-control" placeholder=(t 'formCommand.workingDir.placeholder')}} + {{/input-or-display}}
    - {{input type="text" value=instance.user classNames="form-control" placeholder=(t 'formCommand.user.placeholder')}} + {{#input-or-display editable=editing value=instance.user}} + {{input type="text" value=instance.user classNames="form-control" placeholder=(t 'formCommand.user.placeholder')}} + {{/input-or-display}}
    @@ -35,29 +43,33 @@ -
    -
    - + {{#input-or-display editable=editing value=terminal.name}} +
    +
    + +
    -
    -
    -
    - +
    +
    + +
    -
    + {{/input-or-display}}
    - -
    -
    - + {{#if editing}} + +
    +
    + +
    -
    -
    -
    - +
    +
    + +
    -
    + {{/if}}
    {{#if isService}} @@ -66,16 +78,24 @@
    -
    -
    - + {{#if editing}} +
    +
    + +
    -
    -
    -
    - +
    +
    + +
    -
    + {{else}} + {{#if startOnce}} +
    {{startOnce}}
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} + {{/if}}
    {{else}}
    @@ -83,37 +103,49 @@
    -
    -
    - + {{#if editing}} +
    +
    + +
    -
    -
    -
    - +
    +
    + +
    -
    + {{else}} +
    + {{#if restart}} +
    {{restart}}
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} +
    + {{/if}}
    -
    - -
    -
    - + {{#if editing}} +
    + +
    +
    + +
    +
    +
    +
    + +
    -
    -
    - -
    -
    -
    + {{/if}} {{/if}}
    @@ -131,6 +163,7 @@ valueLabel="formCommand.environment.valueLabel" valuePlaceholder="formCommand.environment.valuePlaceholder" allowEmptyValue=true + editing=editing }}
    diff --git a/app/components/form-healthcheck/template.hbs b/app/components/form-healthcheck/template.hbs index 26d83b80a..590903d0f 100644 --- a/app/components/form-healthcheck/template.hbs +++ b/app/components/form-healthcheck/template.hbs @@ -3,15 +3,23 @@
    -
    - -
    -
    - -
    -
    - -
    + {{#if editing}} +
    + +
    +
    + +
    +
    + +
    + {{else}} + {{#if checkType}} +
    {{checkType}}
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} + {{/if}}
    @@ -21,7 +29,9 @@
    - {{input type="number" min="1" max="65535" classNames="form-control" placeholder=(t 'formHealthCheck.port.placeholder') value=healthCheck.port}} + {{#input-or-display editable=editing value=healthCheck.port}} + {{input type="number" min="1" max="65535" classNames="form-control" placeholder=(t 'formHealthCheck.port.placeholder') value=healthCheck.port}} + {{/input-or-display}}
    @@ -31,37 +41,39 @@
    -
    -
    - - -
    + {{#input-or-display editable=editing value=healthCheck.requestLine}} +
    +
    + + +
    - {{input type="text" classNames="form-control" placeholder=(t 'formHealthCheck.path.placeholder') value=uriPath}} + {{input type="text" classNames="form-control" placeholder=(t 'formHealthCheck.path.placeholder') value=uriPath}} -
    - - -
    -
    +
    + + +
    +
    + {{/input-or-display}}
    {{#if showUriHost}} @@ -70,7 +82,9 @@
    - {{input type="text" classNames="form-control" placeholder=(t 'formHealthCheck.host.placeholder') value=uriHost}} + {{#input-or-display editable=editing value=uriHost}} + {{input type="number" min="1" max="65535" classNames="form-control" placeholder=(t 'formHealthCheck.port.placeholder') value=healthCheck.port}} + {{/input-or-display}}
    {{/if}} @@ -82,19 +96,23 @@
    -
    - {{input type="number" step=100 classNames="form-control" value=healthCheck.initializingTimeout}} - {{t 'formHealthCheck.initializingTimeout.unit'}} -
    + {{#input-or-display editable=editing value=healthCheck.initializingTimeout}} +
    + {{input type="number" step=100 classNames="form-control" value=healthCheck.initializingTimeout}} + {{t 'formHealthCheck.initializingTimeout.unit'}} +
    + {{/input-or-display}}
    -
    - {{input type="number" step=100 classNames="form-control" value=healthCheck.reinitializingTimeout}} - {{t 'formHealthCheck.reinitializingTimeout.unit'}} -
    + {{#input-or-display editable=editing value=healthCheck.reinitializingTimeout}} +
    + {{input type="number" step=100 classNames="form-control" value=healthCheck.reinitializingTimeout}} + {{t 'formHealthCheck.reinitializingTimeout.unit'}} +
    + {{/input-or-display}}
    @@ -104,19 +122,23 @@
    -
    - {{input type="number" min=1 step=1000 classNames="form-control" value=healthCheck.interval}} - {{t 'formHealthCheck.interval.unit'}} -
    + {{#input-or-display editable=editing value=healthCheck.interval}} +
    + {{input type="number" min=1 step=1000 classNames="form-control" value=healthCheck.interval}} + {{t 'formHealthCheck.interval.unit'}} +
    + {{/input-or-display}}
    -
    - {{input type="number" min=1 step=100 classNames="form-control" value=healthCheck.responseTimeout}} - {{t 'formHealthCheck.timeout.unit'}} -
    + {{#input-or-display editable=editing value=healthCheck.responseTimeout}} +
    + {{input type="number" min=1 step=100 classNames="form-control" value=healthCheck.responseTimeout}} + {{t 'formHealthCheck.timeout.unit'}} +
    + {{/input-or-display}}
    @@ -125,19 +147,23 @@
    -
    - {{input type="number" min=1 step=1 classNames="form-control" value=healthCheck.healthyThreshold}} - {{t 'formHealthCheck.healthyThreshold.unit'}} -
    + {{#input-or-display editable=editing value=healthCheck.healthyThreshold}} +
    + {{input type="number" min=1 step=1 classNames="form-control" value=healthCheck.healthyThreshold}} + {{t 'formHealthCheck.healthyThreshold.unit'}} +
    + {{/input-or-display}}
    -
    - {{input type="number" min=1 step=1 classNames="form-control" value=healthCheck.unhealthyThreshold}} - {{t 'formHealthCheck.unhealthyThreshold.unit'}} -
    + {{#input-or-display editable=editing value=healthCheck.unhealthyThreshold}} +
    + {{input type="number" min=1 step=1 classNames="form-control" value=healthCheck.unhealthyThreshold}} + {{t 'formHealthCheck.unhealthyThreshold.unit'}} +
    + {{/input-or-display}}
    @@ -147,6 +173,7 @@
    + {{#input-or-display editable=editing value=strategy}}
    @@ -163,6 +190,7 @@
    + {{/input-or-display}} {{/if}} diff --git a/app/components/form-key-value/component.js b/app/components/form-key-value/component.js index acf2db498..5e73a4589 100644 --- a/app/components/form-key-value/component.js +++ b/app/components/form-key-value/component.js @@ -58,18 +58,18 @@ function removeEmptyEntries(ary, allowEmptyValue=false) { export default Ember.Component.extend({ // Inputs - initialStr : null, - initialMap : null, - addActionLabel : 'formKeyValue.addAction', - keyLabel : 'formKeyValue.key.label', - valueLabel : 'formKeyValue.value.label', - keyPlaceholder : 'formKeyValue.key.placeholder', - valuePlaceholder : 'formKeyValue.value.placeholder', - allowEmptyValue : false, - addInitialEmptyRow : false, - allowMultilineValue : true, - - ary : null, + initialStr: null, + initialMap: null, + addActionLabel: 'formKeyValue.addAction', + keyLabel: 'formKeyValue.key.label', + valueLabel: 'formKeyValue.value.label', + keyPlaceholder: 'formKeyValue.key.placeholder', + valuePlaceholder: 'formKeyValue.value.placeholder', + allowEmptyValue: false, + addInitialEmptyRow: false, + allowMultilineValue: true, + editing: true, + ary: null, actions: { add() { diff --git a/app/components/form-key-value/template.hbs b/app/components/form-key-value/template.hbs index 9ced76d82..b92fd109e 100644 --- a/app/components/form-key-value/template.hbs +++ b/app/components/form-key-value/template.hbs @@ -1,4 +1,6 @@ - +{{#if editing}} + +{{/if}} {{#if ary.length}} @@ -11,28 +13,46 @@ {{#each ary as |row|}} {{/each}}
    - {{input-paste pasted="pastedLabels" class="form-control input-sm key" type="text" value=row.key placeholder=keyPlaceholder}} + {{#if editing}} + {{input-paste pasted="pastedLabels" class="form-control input-sm key" type="text" value=row.key placeholder=keyPlaceholder}} + {{else}} + {{row.key}} + {{/if}} -

    {{t 'formKeyValue.separator'}}

    + {{#if editing}} +

    {{t 'formKeyValue.separator'}}

    + {{/if}}
    - {{#if allowMultilineValue}} - {{textarea-autogrow class="form-control input-sm value" value=row.value placeholder=valuePlaceholder}} + {{#if editing}} + {{#if allowMultilineValue}} + {{textarea-autogrow class="form-control input-sm value" value=row.value placeholder=valuePlaceholder}} + {{else}} + {{input class="form-control input-sm value" type="text" value=row.value placeholder=valuePlaceholder}} + {{/if}} {{else}} - {{input class="form-control input-sm value" type="text" value=row.value placeholder=valuePlaceholder}} + {{row.value}} {{/if}} - + {{#if editing}} + + {{/if}}
    -
    - {{t 'formKeyValue.protip'}} -
    + {{#if editing}} +
    + {{t 'formKeyValue.protip'}} +
    + {{/if}} +{{else}} + {{#unless editing}} +
    {{t 'generic.none'}}
    + {{/unless}} {{/if}} diff --git a/app/components/form-networking/component.js b/app/components/form-networking/component.js index a70ad74c1..d53756ad2 100644 --- a/app/components/form-networking/component.js +++ b/app/components/form-networking/component.js @@ -14,6 +14,7 @@ export default Ember.Component.extend(ManageLabels, ContainerChoices,{ initialLabels : null, tagName : '', + editing: true, init() { this._super(...arguments); diff --git a/app/components/form-networking/template.hbs b/app/components/form-networking/template.hbs index 408089434..e6a73451f 100644 --- a/app/components/form-networking/template.hbs +++ b/app/components/form-networking/template.hbs @@ -4,12 +4,14 @@
    - {{new-select - classNames="form-control" - content=networkChoices - localizedLabel=true - value=instance.networkMode - }} + {{#input-or-display editable=editing value=instance.networkMode}} + {{new-select + classNames="form-control" + content=networkChoices + localizedLabel=true + value=instance.networkMode + }} + {{/input-or-display}}
    {{#if isContainerNetwork}} @@ -17,14 +19,16 @@
    - {{new-select - classNames="form-control" - content=containersOnRequestedHost - optionLabelPath="name" - optionValuePath="id" - optionGroupPath="group" - value=instance.networkContainerId - }} + {{#input-or-display editable=editing value=instance.networkContainerId}} + {{new-select + classNames="form-control" + content=containersOnRequestedHost + optionLabelPath="name" + optionValuePath="id" + optionGroupPath="group" + value=instance.networkContainerId + }} + {{/input-or-display}}
    {{/if}} @@ -36,8 +40,10 @@
    - {{input type="text" value=requestedIp classNames="form-control" placeholder=(t 'formNetwork.requestedIp.placeholder')}} -

    {{t 'formNetwork.requestedIp.help'}}

    + {{#input-or-display editable=editing value=requestedIp}} + {{input type="text" value=requestedIp classNames="form-control" placeholder=(t 'formNetwork.requestedIp.placeholder')}} +

    {{t 'formNetwork.requestedIp.help'}}

    + {{/input-or-display}}
    {{#if isService}} @@ -47,7 +53,9 @@
    - + {{#input-or-display editable=editing value=service.retainIp}} + + {{/input-or-display}}
    {{/if}} @@ -59,14 +67,46 @@
    - + {{#input-or-display editable=editing value=dnsDiscovery}} + + {{/input-or-display}}
    {{/if}} {{/if}} {{#unless isService}} - {{form-container-links initialLinks=instance.instanceLinks instance=instance allHosts=allHosts}} + {{#if editing}} + {{form-container-links initialLinks=instance.instanceLinks instance=instance allHosts=allHosts}} + {{else}} +
    +
    + +
    +
    + {{#if instance.instanceLinks}} + + + + + + + + + {{#each-in instance.instanceLinks as |key value|}} + + + + + {{/each-in}} + +
    {{t 'formNetwork.links.table.dest'}}{{t 'formNetwork.links.table.as'}}
    {{key}}{{value}}
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} +
    +
    + {{/if}} {{/unless}}
    @@ -76,18 +116,22 @@
    {{#if isService}} -
    - -
    -
    - -
    -
    - - {{input type="text" value=instance.hostname classNames="form-control r-ml10 input-sm" safeStyle="display:inline; width: 200px;" placeholder=(t 'formNetwork.hostname.placeholder') disabled=(not-eq hostname "custom")}} -
    + {{#input-or-display editable=editing value=hostname}} +
    + +
    +
    + +
    +
    + + {{input type="text" value=instance.hostname classNames="form-control r-ml10 input-sm" safeStyle="display:inline; width: 200px;" placeholder=(t 'formNetwork.hostname.placeholder') disabled=(not-eq hostname "custom")}} +
    + {{/input-or-display}} {{else}} - {{input type="text" value=instance.hostname classNames="form-control" placeholder=(t 'formNetwork.hostname.placeholder')}} + {{#input-or-display editable=editing value=instance.hostname}} + {{input type="text" value=instance.hostname classNames="form-control" placeholder=(t 'formNetwork.hostname.placeholder')}} + {{/input-or-display}} {{/if}}
    @@ -98,7 +142,9 @@
    - {{input type="text" value=instance.domainName classNames="form-control" placeholder=(t 'formNetwork.domainName.placeholder')}} + {{#input-or-display editable=editing value=instance.domainName}} + {{input type="text" value=instance.domainName classNames="form-control" placeholder=(t 'formNetwork.domainName.placeholder')}} + {{/input-or-display}}
    @@ -108,22 +154,41 @@
    -
    - -
    - {{#if dnsResolverArray.length}} - - {{#each dnsResolverArray as |dns|}} - - - - - {{/each}} -
    - {{input type="text" value=dns.value classNames="form-control dns-value input-sm" placeholder=(t 'formNetwork.resolvingServers.placeholder')}} - - -
    + {{#if editing}} +
    + +
    + {{#if dnsResolverArray.length}} + + {{#each dnsResolverArray as |dns|}} + + + + + {{/each}} +
    + {{input type="text" value=dns.value classNames="form-control dns-value input-sm" placeholder=(t 'formNetwork.resolvingServers.placeholder')}} + + +
    + {{/if}} + {{else}} + {{#if dnsResolverArray.length}} + + {{#each dnsResolverArray as |dns|}} + + + + + {{/each}} +
    + {{dns.value}} + +   +
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} {{/if}}
    @@ -134,22 +199,41 @@
    -
    - -
    - {{#if dnsSearchArray.length}} - - {{#each dnsSearchArray as |dnsSearch|}} - - - - - {{/each}} -
    - {{input type="text" value=dnsSearch.value classNames="form-control dns-search-value input-sm" placeholder=(t 'formNetwork.searchDomains.placeholder')}} - - -
    + {{#if editing}} +
    + +
    + {{#if dnsSearchArray.length}} + + {{#each dnsSearchArray as |dnsSearch|}} + + + + + {{/each}} +
    + {{input type="text" value=dnsSearch.value classNames="form-control dns-search-value input-sm" placeholder=(t 'formNetwork.searchDomains.placeholder')}} + + +
    + {{/if}} + {{else}} + {{#if dnsSearchArray.length}} + + {{#each dnsSearchArray as |dnsSearch|}} + + + + + {{/each}} +
    + {{dnsSearch.value}} + +   +
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} {{/if}}
    diff --git a/app/components/form-scheduling/component.js b/app/components/form-scheduling/component.js index cff8bb28b..11384d59a 100644 --- a/app/components/form-scheduling/component.js +++ b/app/components/form-scheduling/component.js @@ -105,6 +105,10 @@ export default Ember.Component.extend(ManageLabels, { this.sendAction('setRequestedHost', hostId); }.observes('requestedHostId'), + selectedChoice: Ember.computed('allHosts.@each.{id,name,state}', function() { + return this.get('hostChoices').findBy('id', this.get('initialHostId')); + }), + hostChoices: function() { var list = this.get('allHosts').map((host) => { var hostLabel = host.get('displayName'); diff --git a/app/components/form-scheduling/template.hbs b/app/components/form-scheduling/template.hbs index 9877d8e16..f37f003f7 100644 --- a/app/components/form-scheduling/template.hbs +++ b/app/components/form-scheduling/template.hbs @@ -1,47 +1,65 @@
    {{#if canRequestHost}} -
    - - {{#if isRequestedHost}} - {{new-select - class="form-control" - content=hostChoices - value=requestedHostId - optionValuePath="id" - optionLabelPath="name" - disabled=(not isRequestedHost) - style="display: inline-block; width: auto;" - }} - {{/if}} -
    - -
    -
    + +
    + +
    + {{else}} + {{#if requestedHostId}} + + {{selectedChoice.name}} + {{/if}} + {{/if}} {{else}} {{#if isService}} {{#if isGlobal}} @@ -59,9 +77,11 @@ {{/if}} {{#unless isRequestedHost}} -
    - -
    + {{#if editing}} +
    + +
    + {{/if}} {{#if affinityLabelArray.length}} @@ -80,7 +100,12 @@ {{#each labelArray as |rule|}} {{#if (eq rule.type "affinity")}} - {{scheduling-rule-row rule=rule allHosts=allHosts remove="removeSchedulingRule" isGlobal=isGlobal}} + {{scheduling-rule-row + editing=editing + rule=rule + allHosts=allHosts + remove="removeSchedulingRule" + isGlobal=isGlobal}} {{/if}} {{/each}} diff --git a/app/components/form-security/component.js b/app/components/form-security/component.js index baa5ceb86..0d32b64a2 100644 --- a/app/components/form-security/component.js +++ b/app/components/form-security/component.js @@ -273,4 +273,8 @@ export default Ember.Component.extend({ 'splunk', 'syslog', ], + + hasLogConfig: Ember.computed('instance.logConfig.config', function() { + return Ember.isEmpty(this.get('instance.logConfig.config')); + }), }); diff --git a/app/components/form-security/template.hbs b/app/components/form-security/template.hbs index b29e39803..46f0046a3 100644 --- a/app/components/form-security/template.hbs +++ b/app/components/form-security/template.hbs @@ -3,18 +3,22 @@
    -
    - -
    + {{#input-or-display editable=editing value=instance.privileged}} +
    + +
    + {{/input-or-display}}
    -
    - -
    + {{#input-or-display editable=editing value=pidHost}} +
    + +
    + {{/input-or-display}}
    @@ -23,20 +27,24 @@
    -
    - {{input type="number" min="1" step="1" value=memoryMb classNames="form-control" placeholder=(t 'formSecurity.memoryLimit.placeholder')}} -
    {{t 'formSecurity.memoryLimit.mb'}}
    -
    + {{#input-or-display editable=editing value=memoryMb}} +
    + {{input type="number" min="1" step="1" value=memoryMb classNames="form-control" placeholder=(t 'formSecurity.memoryLimit.placeholder')}} +
    {{t 'formSecurity.memoryLimit.mb'}}
    +
    + {{/input-or-display}}
    -
    - {{input type="number" min="1" step="1" value=swapMb classNames="form-control" placeholder=(if memoryMb 'Requires Memory Limit' 'Unlimited') disabled=(not memoryMb)}} -
    {{t 'formSecurity.swapLimit.mb'}}
    -
    + {{#input-or-display editable=editing value=swapMb}} +
    + {{input type="number" min="1" step="1" value=swapMb classNames="form-control" placeholder=(if memoryMb 'Requires Memory Limit' 'Unlimited') disabled=(not memoryMb)}} +
    {{t 'formSecurity.swapLimit.mb'}}
    +
    + {{/input-or-display}}
    @@ -46,45 +54,70 @@
    - {{input type="text" value=instance.cpuSet classNames="form-control" placeholder=(t 'formSecurity.cpuPinning.placeholder')}} + {{#input-or-display editable=false value=instance.cpuSet}} + {{input type="text" value=instance.cpuSet classNames="form-control" placeholder=(t 'formSecurity.cpuPinning.placeholder')}} + {{/input-or-display}}
    - {{input type="number" value=instance.cpuShares step=128 min=0 classNames="form-control" placeholder=(t 'formSecurity.shares.placeholder')}} + {{#input-or-display editable=editing value=instance.cpuShares}} + {{input type="number" value=instance.cpuShares step=128 min=0 classNames="form-control" placeholder=(t 'formSecurity.shares.placeholder')}} + {{/input-or-display}}
    -
    - + {{#if editing}} + + {{else}} + {{#if instance.capAdd}} + {{#each instance.capAdd as |choice index|}} + {{if index ", "}} {{choice}} + {{/each}} + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} + {{/if}}
    - + {{#if editing}} + + {{else}} + {{#if instance.capDrop}} + {{#each instance.capDrop as |choice index|}} + {{if index ", "}} {{choice}} + {{/each}} + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} + {{/if}}
    -
    -
    - - {{t 'formSecurity.capabilities.helpBlock.text'}} - {{t 'formSecurity.capabilities.helpBlock.link'}} - +{{#if editing}} +
    +
    + + {{t 'formSecurity.capabilities.helpBlock.text'}} + {{t 'formSecurity.capabilities.helpBlock.link'}} + +
    -
    +{{/if}}
    @@ -92,40 +125,77 @@
    -
    - -
    - {{#if devicesArray.length}} -
    - - - - - - - - - {{#each devicesArray as |device|}} - - - - - - - + {{#if editing}} +
    + +
    + {{#if devicesArray.length}} +
    {{t 'formSecurity.deviceBinding.pathHost.label'}} {{t 'formSecurity.deviceBinding.pathContainer.label'}} {{t 'formSecurity.deviceBinding.permissions.label'}} 
    - {{input class="form-control input-sm device-host" type="text" value=device.host placeholder=(t 'formSecurity.deviceBinding.pathHost.placeholder')}} - -

    -
    - {{input class="form-control input-sm" type="text" value=device.container placeholder=(t 'formSecurity.deviceBinding.pathContainer.placeholder')}} -   - {{device-permissions buttonClass="btn-sm" initialSelection=device.permissions changed=(action (mut device.permissions))}} - - -
    + + + + + + + - {{/each}} -
    {{t 'formSecurity.deviceBinding.pathHost.label'}} {{t 'formSecurity.deviceBinding.pathContainer.label'}} {{t 'formSecurity.deviceBinding.permissions.label'}} 
    + {{#each devicesArray as |device|}} + + + {{input class="form-control input-sm device-host" type="text" value=device.host placeholder=(t 'formSecurity.deviceBinding.pathHost.placeholder')}} + + +

    + + + {{input class="form-control input-sm" type="text" value=device.container placeholder=(t 'formSecurity.deviceBinding.pathContainer.placeholder')}} + +   + + {{device-permissions buttonClass="btn-sm" initialSelection=device.permissions changed=(action (mut device.permissions))}} + + + + + + {{/each}} + + {{/if}} + {{else}} + {{#if devicesArray.length}} + + + + + + + + + + {{#each devicesArray as |device|}} + + + + + + + + + {{/each}} +
    {{t 'formSecurity.deviceBinding.pathHost.label'}} {{t 'formSecurity.deviceBinding.pathContainer.label'}} {{t 'formSecurity.deviceBinding.permissions.label'}} 
    + {{device.host}} + +

    +
    + {{device.container}} +   + {{device-permissions buttonClass="btn-sm" initialSelection=device.permissions changed=(action (mut device.permissions)) editing=editing}} + +   +
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} {{/if}}
    @@ -136,17 +206,19 @@
    -
    - {{input type="text" value=instance.logConfig.driver classNames="form-control" placeholder=(t 'formSecurity.logDriver.placeholder')}} -
    - - + {{#input-or-display editable=editing value=instance.logConfig.driver}} +
    + {{input type="text" value=instance.logConfig.driver classNames="form-control" placeholder=(t 'formSecurity.logDriver.placeholder')}} +
    + + +
    -
    + {{/input-or-display}}
    @@ -162,6 +234,7 @@ addActionLabel="formSecurity.logConfig.addActionLabel" keyPlaceholder="formSecurity.logConfig.keyPlaceholder" valuePlaceholder="formSecurity.logConfig.valuePlaceholder" + editing=editing }} diff --git a/app/components/info-multi-stats/component.js b/app/components/info-multi-stats/component.js index efa79e6f3..cdc8554c2 100644 --- a/app/components/info-multi-stats/component.js +++ b/app/components/info-multi-stats/component.js @@ -31,6 +31,7 @@ export default Ember.Component.extend({ linkName: 'containerStats', single: true, showGraphs: true, + showMultiStat: true, renderSeconds: null, diff --git a/app/components/info-multi-stats/template.hbs b/app/components/info-multi-stats/template.hbs index 3ce703214..9fe0fb03c 100644 --- a/app/components/info-multi-stats/template.hbs +++ b/app/components/info-multi-stats/template.hbs @@ -1,6 +1,8 @@ -
    - {{yield}} -
    +{{#if showMultiStat}} +
    + {{yield}} +
    +{{/if}} {{#if showGraphs}}
    diff --git a/app/components/input-or-display/component.js b/app/components/input-or-display/component.js new file mode 100644 index 000000000..03fef44ce --- /dev/null +++ b/app/components/input-or-display/component.js @@ -0,0 +1,9 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + tagName: '', + value: null, + editable: true, + classesForInput: 'form-control', + classesForDisplay: 'form-control-static', +}); diff --git a/app/components/input-or-display/template.hbs b/app/components/input-or-display/template.hbs new file mode 100644 index 000000000..6548246d7 --- /dev/null +++ b/app/components/input-or-display/template.hbs @@ -0,0 +1,9 @@ +{{#if editable}} + {{yield}} +{{else}} + {{#if value}} +
    {{value}}
    + {{else}} +
    {{t 'generic.none'}}
    + {{/if}} +{{/if}} diff --git a/app/components/new-container/template.hbs b/app/components/new-container/template.hbs index d8f2ecddf..86828ccf6 100644 --- a/app/components/new-container/template.hbs +++ b/app/components/new-container/template.hbs @@ -136,7 +136,7 @@
    {{form-networking - editing=editing + editing=true instance=launchConfig errors=networkingErrors allHosts=allHosts @@ -148,12 +148,16 @@
    - {{form-security instance=launchConfig errors=securityErrors}} + {{form-security instance=launchConfig errors=securityErrors editing=true}}
    {{/if}}
    - {{form-healthcheck isService=isService healthCheck=launchConfig.healthCheck errors=healthCheckErrors}} + {{form-healthcheck + isService=isService + healthCheck=launchConfig.healthCheck + errors=healthCheckErrors + editing=true}}
    @@ -171,6 +175,7 @@ initialHostId=launchConfig.requestedHostId initialLabels=launchConfig.labels errors=schedulingErrors + editing=true allHosts=allHosts setLabels=(action 'setLabels' 'scheduling') setGlobal=(action 'setGlobal') diff --git a/app/components/scheduling-rule-row/component.js b/app/components/scheduling-rule-row/component.js index 4043469f9..d0f9da22e 100644 --- a/app/components/scheduling-rule-row/component.js +++ b/app/components/scheduling-rule-row/component.js @@ -181,6 +181,12 @@ export default Ember.Component.extend({ } }.observes('isGlobal'), + getSuffixLabel: Ember.computed('suffix', function() { + let label = this.get('schedulingRuleSuffixChoices').findBy('value', this.get('suffix')).label; + label = label.split('.'); + return label[label.length -1]; + }), + schedulingRuleSuffixChoices: function() { var out = [ {label: 'schedulingRuleRow.must', value: ''}, diff --git a/app/components/scheduling-rule-row/template.hbs b/app/components/scheduling-rule-row/template.hbs index c75e0d7bf..80e92ceca 100644 --- a/app/components/scheduling-rule-row/template.hbs +++ b/app/components/scheduling-rule-row/template.hbs @@ -1,20 +1,28 @@
    {{t 'schedulingRuleRow.theHost'}}
    - {{new-select - classNames="form-control input-sm" - content=schedulingRuleSuffixChoices - localizedLabel=true - value=suffix - }} + {{#if editing}} + {{new-select + classNames="form-control input-sm" + content=schedulingRuleSuffixChoices + localizedLabel=true + value=suffix + }} + {{else}} +
    {{getSuffixLabel}}
    + {{/if}}
    {{t 'schedulingRuleRow.haveA'}}
    - {{new-select - classNames="form-control input-sm" - content=schedulingRuleKindChoices - localizedLabel=true - value=kind - }} + {{#if editing}} + {{new-select + classNames="form-control input-sm" + content=schedulingRuleKindChoices + localizedLabel=true + value=kind + }} + {{else}} +
    {{kind}}
    + {{/if}} {{#if (not (or (eq kind "service_name") (eq kind "container_name")))}} @@ -23,30 +31,38 @@ {{#if (eq kind "host_label")}} -
    - {{input type="text" class="form-control input-sm" value=userKey}} -
    - - + {{#if editing}} +
    + {{input type="text" class="form-control input-sm" value=userKey}} +
    + + +
    -
    + {{else}} +
    {{userKey}}
    + {{/if}} {{/if}} {{#if (eq kind "container_label")}} -
    - {{input type="text" class="form-control input-sm" value=userKey}} -
    - - + {{#if editing}} +
    + {{input type="text" class="form-control input-sm" value=userKey}} +
    + + +
    -
    + {{else}} +
    {{userKey}}
    + {{/if}} {{/if}} @@ -56,66 +72,84 @@ {{#if (eq kind "host_label")}} -
    - {{input type="text" class="form-control input-sm" value=userValue}} - {{#if hostLabelValueChoices.length}} -
    - - -
    - {{/if}} -
    + {{#if editing}} +
    + {{input type="text" class="form-control input-sm" value=userValue}} + {{#if hostLabelValueChoices.length}} +
    + + +
    + {{/if}} +
    + {{else}} +
    {{userValue}}
    + {{/if}} {{/if}} {{#if (eq kind "container_label")}} -
    - {{input type="text" class="form-control input-sm" value=userValue}} - {{#if containerLabelValueChoices.length}} -
    - - -
    - {{/if}} -
    + {{#if editing}} +
    + {{input type="text" class="form-control input-sm" value=userValue}} + {{#if containerLabelValueChoices.length}} +
    + + +
    + {{/if}} +
    + {{else}} +
    {{userValue}}
    + {{/if}} {{/if}} {{#if (eq kind "service_name")}} -
    - {{input type="text" class="form-control input-sm" value=userValue}} - {{#if serviceValueChoices.length}} -
    - - -
    - {{/if}} -
    + {{#if editing}} +
    + {{input type="text" class="form-control input-sm" value=userValue}} + {{#if serviceValueChoices.length}} +
    + + +
    + {{/if}} +
    + {{else}} +
    {{userValue}}
    + {{/if}} {{/if}} {{#if (eq kind "container_name")}} -
    - {{input type="text" class="form-control input-sm" value=userValue}} - {{#if containerValueChoices.length}} -
    - - -
    - {{/if}} -
    + {{#if editing}} +
    + {{input type="text" class="form-control input-sm" value=userValue}} + {{#if containerValueChoices.length}} +
    + + +
    + {{/if}} +
    + {{else}} +
    {{userValue}}
    + {{/if}} {{/if}} - - - +{{#if editing}} + + + +{{/if}} diff --git a/app/components/snapshot-section/template.hbs b/app/components/snapshot-section/template.hbs index 58e0d7de7..e00531ea6 100644 --- a/app/components/snapshot-section/template.hbs +++ b/app/components/snapshot-section/template.hbs @@ -7,25 +7,6 @@ {{snapshot.name}} {{/tooltip-element}} - - {{#if (gt snapshot.backupCount 1)}} - {{#if snapshot.latestCompleteBackup}} - {{t 'snapshotSection.multiple' latestDate=(date-str snapshot.latestCompleteBackup.created)}} - {{else}} - {{badge-state model=snapshot.latestBackup}} - {{/if}} - {{else}} - {{#if (eq snapshot.backupCount 1)}} - {{#if snapshot.latestCompleteBackup}} - {{date-str snapshot.latestCompleteBackup.created}} - {{else}} - {{badge-state model=snapshot.latestBackup}} - {{/if}} - {{else}} - {{t 'snapshotSection.none'}} - {{/if}} - {{/if}} - {{action-menu model=snapshot}} diff --git a/app/components/snapshot-timeline/component.js b/app/components/snapshot-timeline/component.js new file mode 100644 index 000000000..efaae393a --- /dev/null +++ b/app/components/snapshot-timeline/component.js @@ -0,0 +1,82 @@ +import Ember from 'ember'; +import ThrottledResize from 'ui/mixins/throttled-resize'; + +const { get, set, computed } = Ember; +const SHIFT_DIFF = 3; +const PLOT_HEIGHT = 7; + +export default Ember.Component.extend(ThrottledResize, { + tagName: 'div', + classNames: ['timeline-container'], + startDate: null, + endDate: null, + range: null, + + init: function() { + this._super(...arguments); + + if (!get(this, 'model')) { + set(this, 'classNames', []); + } + + }, + + onResize: function() { + if (get(this, 'model')) { + this.plotSnapshots(); + } + }, + + snapshots: computed('model', function() { + return this.plotSnapshots(); + }), + + plotSnapshots() { + let prevPlot = null; + let snapshots = get(this, 'model').sortBy('created'); + let model = get(this, 'model'); + let start = model[0].created; + let end = model[model.length - 1].created; + let range = moment(end).diff(start, 'seconds'); + + snapshots.forEach((snapshot) => { + let myDate = snapshot.created; + let fromStart = moment(myDate).diff(start, 'seconds'); + let shift = (fromStart/range) * 100; + + if (prevPlot !== null && prevPlot >= 0 && shift !== 100) { + if ((shift - prevPlot) <= SHIFT_DIFF) { + shift = prevPlot + SHIFT_DIFF; + } + } + + prevPlot = shift; + + set(snapshot, 'position', Ember.String.htmlSafe(`left: calc(${shift}% - ${PLOT_HEIGHT});`)); + return snapshot; + }); + + prevPlot = null; + + snapshots.reverse().forEach((snapshot) => { + + let myDate = snapshot.created; + let fromStart = moment(myDate).diff(start, 'seconds'); + let shift = (fromStart/range) * 100; + + if (prevPlot !== null && shift >= 0) { + if (Math.abs(shift - prevPlot) <= SHIFT_DIFF) { + shift = Math.max(0, prevPlot - SHIFT_DIFF); + } + } + + prevPlot = shift; + + set(snapshot, 'position', Ember.String.htmlSafe(`left: calc(${shift}% - ${PLOT_HEIGHT}px);`)); + return snapshot; + }); + + + return snapshots.reverse(); + }, +}); diff --git a/app/components/snapshot-timeline/template.hbs b/app/components/snapshot-timeline/template.hbs new file mode 100644 index 000000000..17ebac9f3 --- /dev/null +++ b/app/components/snapshot-timeline/template.hbs @@ -0,0 +1,13 @@ +{{#each snapshots as |snapshot|}} +
    + {{#if (or (eq snapshot.state 'transitioning') (eq snapshot.state 'backingup'))}} + {{#tooltip-element type='tooltip-action-menu' model=snapshot tooltipTemplate='tooltip-snapshot-timeline' tagName="div" classNames='timeline-child'}} + + {{/tooltip-element}} + {{else}} + {{tooltip-element type='tooltip-action-menu' model=snapshot tooltipTemplate='tooltip-snapshot-timeline' tagName="div" classNames='timeline-child'}} + {{/if}} +
    +{{else}} +
    {{t 'snapshotTimeline.noData'}}
    +{{/each}} diff --git a/app/components/storagepool-section/template.hbs b/app/components/storagepool-section/template.hbs index d2f087d7c..6bbc9b352 100644 --- a/app/components/storagepool-section/template.hbs +++ b/app/components/storagepool-section/template.hbs @@ -90,8 +90,7 @@ {{t 'storagePoolSection.models.table.header.snapshotState'}} {{t 'storagePoolSection.models.table.header.snapshotName'}} - {{t 'storagePoolSection.models.table.header.backedUp'}} -   +   diff --git a/app/components/tooltip-action-menu/component.js b/app/components/tooltip-action-menu/component.js index 924532252..589195234 100644 --- a/app/components/tooltip-action-menu/component.js +++ b/app/components/tooltip-action-menu/component.js @@ -9,13 +9,18 @@ export default Ember.Component.extend(Tooltip, StrippedName, { model : Ember.computed.alias('tooltipService.tooltipOpts.model'), actionsOpen : Ember.computed.alias('resourceActions.open'), inTooltip : false, + layoutName: 'tooltip-action-menu', - setup: function() { + init: function() { + if (this.get('tooltipTemplate')) { + this.set('layoutName', this.get('tooltipTemplate')); + } + this._super(...arguments); // Just so openChanged is ready to go, otherwise you have to chain on('init') on openChanged // which because of the context menu click on container dot can cause some issues with checking // flags and such. This was the least compliated way to ensure that openChanged would recognize changes this.set('actionsOpen', false); - }.on('init'), + }, mouseEnter: function() { this._super(); diff --git a/app/components/tooltip-action-menu/template.hbs b/app/components/tooltip-action-menu/template.hbs deleted file mode 100644 index 1d0d36c40..000000000 --- a/app/components/tooltip-action-menu/template.hbs +++ /dev/null @@ -1,5 +0,0 @@ -
    - {{action-menu model=model showPrimary=false inTooltip=true class="pull-right tooltip-more-actions"}} -
    {{displayName}}
    -
    {{format-ip ip=model.displayIp}}
    -
    diff --git a/app/storagepools/backups/route.js b/app/container/commands/route.js similarity index 62% rename from app/storagepools/backups/route.js rename to app/container/commands/route.js index 27d5ed2b2..e6f86e4e1 100644 --- a/app/storagepools/backups/route.js +++ b/app/container/commands/route.js @@ -2,6 +2,6 @@ import Ember from 'ember'; export default Ember.Route.extend({ model: function() { - return this.get('store').findAllUnremoved('backup'); + return this.modelFor('container').container; } }); diff --git a/app/container/commands/template.hbs b/app/container/commands/template.hbs new file mode 100644 index 000000000..6dd20d086 --- /dev/null +++ b/app/container/commands/template.hbs @@ -0,0 +1,7 @@ +
    + {{form-command + instance=model + initialLabels=model.labels + editing=false + }} +
    diff --git a/app/container/healthcheck/route.js b/app/container/healthcheck/route.js new file mode 100644 index 000000000..e6f86e4e1 --- /dev/null +++ b/app/container/healthcheck/route.js @@ -0,0 +1,7 @@ +import Ember from 'ember'; + +export default Ember.Route.extend({ + model: function() { + return this.modelFor('container').container; + } +}); diff --git a/app/container/healthcheck/template.hbs b/app/container/healthcheck/template.hbs new file mode 100644 index 000000000..15077e693 --- /dev/null +++ b/app/container/healthcheck/template.hbs @@ -0,0 +1,5 @@ +
    + {{form-healthcheck + healthCheck=model.healthCheck + editing=false}} +
    diff --git a/app/container/index/route.js b/app/container/index/route.js index f09d61eac..2746c6765 100644 --- a/app/container/index/route.js +++ b/app/container/index/route.js @@ -2,6 +2,6 @@ import Ember from 'ember'; export default Ember.Route.extend({ redirect: function() { - this.replaceWith('container.labels'); + this.replaceWith('container.ports'); } }); diff --git a/app/container/networking/route.js b/app/container/networking/route.js new file mode 100644 index 000000000..ff923f5ec --- /dev/null +++ b/app/container/networking/route.js @@ -0,0 +1,12 @@ +import Ember from 'ember'; + +export default Ember.Route.extend({ + model: function() { + return this.get('store').findAll('host').then((hosts) => { + return { + hosts: hosts, + container: this.modelFor('container').container, + }; + }); + } +}); diff --git a/app/container/networking/template.hbs b/app/container/networking/template.hbs new file mode 100644 index 000000000..a4f24ff88 --- /dev/null +++ b/app/container/networking/template.hbs @@ -0,0 +1,8 @@ +
    + {{form-networking + editing=false + instance=model.container + initialLabels=model.labels + allHosts=model.hosts + }} +
    diff --git a/app/container/scheduling/route.js b/app/container/scheduling/route.js new file mode 100644 index 000000000..ff923f5ec --- /dev/null +++ b/app/container/scheduling/route.js @@ -0,0 +1,12 @@ +import Ember from 'ember'; + +export default Ember.Route.extend({ + model: function() { + return this.get('store').findAll('host').then((hosts) => { + return { + hosts: hosts, + container: this.modelFor('container').container, + }; + }); + } +}); diff --git a/app/container/scheduling/template.hbs b/app/container/scheduling/template.hbs new file mode 100644 index 000000000..bdc2ced94 --- /dev/null +++ b/app/container/scheduling/template.hbs @@ -0,0 +1,8 @@ +
    + {{form-scheduling + initialHostId=model.container.requestedHostId + initialLabels=model.container.labels + allHosts=model.hosts + editing=false + }} +
    diff --git a/app/container/security/route.js b/app/container/security/route.js new file mode 100644 index 000000000..e6f86e4e1 --- /dev/null +++ b/app/container/security/route.js @@ -0,0 +1,7 @@ +import Ember from 'ember'; + +export default Ember.Route.extend({ + model: function() { + return this.modelFor('container').container; + } +}); diff --git a/app/container/security/template.hbs b/app/container/security/template.hbs new file mode 100644 index 000000000..4ed224704 --- /dev/null +++ b/app/container/security/template.hbs @@ -0,0 +1,3 @@ +
    + {{form-security instance=model editing=false}} +
    diff --git a/app/container/template.hbs b/app/container/template.hbs index 14d1f9233..6466d3409 100644 --- a/app/container/template.hbs +++ b/app/container/template.hbs @@ -6,14 +6,6 @@
    {{/power-select}} - {{#if model.primaryHost}} - {{t 'containersPage.containerPage.subtext.on'}} {{#link-to "host" model.primaryHost.id}}{{model.primaryHost.displayName}}{{/link-to}} - {{/if}} - - {{#if model.primaryService}} - {{t 'containersPage.containerPage.subtext.in'}} {{#link-to "service" model.primaryService.environmentId model.primaryService.id}}{{model.primaryService.displayName}}{{/link-to}} - {{/if}} -
    {{action-menu model=model classNames="r-ml5 pull-right" size="sm"}} {{header-state model=model classNames="pull-right"}} @@ -22,10 +14,19 @@
    - {{#info-multi-stats model=model linkName="containerStats" single=true}} -
    -
    - +
    +
    + {{#if model.description}} +
    + +
    {{model.description}}
    +
    + +
    + {{/if}} + +
    + {{#if model.primaryHost}} {{#link-to "host" model.primaryHost.id}}{{model.primaryHost.displayName}}{{/link-to}} {{copy-to-clipboard clipboardText=model.primaryHost.displayIp tooltipText=(t 'containersPage.containerPage.infoMultiStats.tooltip.host') size="small"}} @@ -33,28 +34,38 @@ {{t 'generic.none'}} {{/if}}
    -
    - + +
    + +
    + {{format-ip ip=model.displayIp showCopy=true}}
    -
    - + +
    + +
    + {{#if model.displayExternalId}} {{model.displayExternalId}} {{copy-to-clipboard clipboardText=model.externalId size='small'}} {{else}} {{t 'generic.none'}} {{/if}}
    -
    -
    -
    - + +
    + +
    + {{model.displayImage}} {{copy-to-clipboard clipboardText=model.displayImage size='small'}}
    -
    - + +
    + +
    + {{#if model.command}} {{concat-str model.command}} @@ -63,8 +74,11 @@ {{/if}}
    -
    - + +
    + +
    + {{#if model.entryPoint}} {{concat-str model.entryPoint}} @@ -74,24 +88,26 @@
    - {{#if model.description}} -
    -
    - - {{model.description}} +
    +
    + {{info-multi-stats model=model linkName="containerStats" single=true showMultiStat=false}} +
    +
    + +
    + {{outlet}}
    - {{/if}} - {{/info-multi-stats}} -
    -
    - -
    - {{outlet}} +
    diff --git a/app/container/volumes/template.hbs b/app/container/volumes/template.hbs index 166a6f2cd..0682a7643 100644 --- a/app/container/volumes/template.hbs +++ b/app/container/volumes/template.hbs @@ -1,36 +1,26 @@ - - - - + + + + {{#each model.activeMounts as |mount|}} - - - {{else}} diff --git a/app/mixins/cattle-transitioning-resource.js b/app/mixins/cattle-transitioning-resource.js index c7e7b93f3..91b0b8420 100644 --- a/app/mixins/cattle-transitioning-resource.js +++ b/app/mixins/cattle-transitioning-resource.js @@ -7,6 +7,7 @@ import C from 'ui/utils/constants'; const defaultStateMap = { 'activating': {icon: 'icon icon-tag', color: 'text-info' }, 'active': {icon: 'icon icon-circle-o', color: 'text-success'}, + 'backedup': {icon: 'icon icon-backup', color: 'text-success'}, 'created': {icon: 'icon icon-tag', color: 'text-info' }, 'creating': {icon: 'icon icon-tag', color: 'text-info' }, 'deactivating': {icon: 'icon icon-adjust', color: 'text-info' }, @@ -23,6 +24,7 @@ const defaultStateMap = { 'reinitializing': {icon: 'icon icon-alert', color: 'text-warning'}, 'restoring': {icon: 'icon icon-medicalcross', color: 'text-info' }, 'running': {icon: 'icon icon-circle-o', color: 'text-success'}, + 'snapshotted': {icon: 'icon icon-snapshot', color: 'text-warning'}, 'started-once': {icon: 'icon icon-dot-circlefill',color: 'text-success'}, 'starting': {icon: 'icon icon-adjust', color: 'text-info' }, 'stopped': {icon: 'icon icon-circle', color: 'text-danger' }, diff --git a/app/models/mount.js b/app/models/mount.js index f20902bdc..d7de08d10 100644 --- a/app/models/mount.js +++ b/app/models/mount.js @@ -2,21 +2,27 @@ import Ember from 'ember'; import Resource from 'ember-api-store/models/resource'; import C from 'ui/utils/constants'; -const { getOwner } = Ember; +const { getOwner, computed } = Ember; let _allMounts; let _allContainers; +let _allVolumes; +let _allSnapshots; var Mount = Resource.extend({ isReadWrite: Ember.computed.equal('permissions','rw'), isReadOnly: Ember.computed.equal('permissions','ro'), - _allMounts : null, - _allContainers : null, + _allMounts: null, + _allContainers: null, + _allVolumes: null, + _allSnapshots: null, reservedKeys: [ '_allMounts', - '_allContainers' + '_allContainers', + '_allVolumes', + '_allSnapshots', ], init: function() { @@ -33,9 +39,20 @@ var Mount = Resource.extend({ _allContainers = store.allUnremoved('container'); } + if ( !_allVolumes ) { + _allVolumes = store.allUnremoved('volume'); + } + + if ( !_allSnapshots ) + { + _allSnapshots = store.allUnremoved('snapshot'); + } + this.setProperties({ '_allMounts' : _allMounts, '_allContainers' : _allContainers, + '_allVolumes' : _allVolumes, + '_allSnapshots': _allSnapshots, }); }, @@ -85,12 +102,23 @@ var Mount = Resource.extend({ return out; }.property('_allContainers.@each.instanceId', 'id'), + + snapshotsAndBackups: computed('_allSnapshots.[]', function() { + let volumeId = this.get('volumeId'); + return this.get('_allSnapshots').get('content').filterBy('volumeId', volumeId); + }), + + ownedVolume: computed('_allVolumes.[]', function() { + return this.get('_allVolumes').findBy('id', this.get('volumeId')); + }), }); Mount.reopenClass({ reset: function() { - _allMounts = null; - _allContainers = null; + _allMounts = null; + _allContainers = null; + _allVolumes = null; + _allSnapshots = null; }, }); diff --git a/app/models/snapshot.js b/app/models/snapshot.js index 1a8a14bdb..f3604e6c1 100644 --- a/app/models/snapshot.js +++ b/app/models/snapshot.js @@ -3,7 +3,6 @@ import Resource from 'ember-api-store/models/resource'; const { getOwner } = Ember; // !! If you add a new one of these, you need to add it to reset() below too -var _allBackups; var _allVolumes; // !! If you add a new one of these, you need to add it to reset() below too @@ -11,11 +10,9 @@ var Snapshot = Resource.extend({ type: 'snapshot', // !! If you add a new one of these, you need to add it to reset() below too - _allBackups: null, _allVolumes: null, reservedKeys: [ - '_allBackups', '_allVolumes', ], @@ -24,10 +21,6 @@ var Snapshot = Resource.extend({ // this.get('store') isn't set yet at init var store = getOwner(this).lookup('store:main'); - if ( !_allBackups ) - { - _allBackups = store.allUnremoved('backup'); - } if ( !_allVolumes ) { @@ -35,7 +28,6 @@ var Snapshot = Resource.extend({ } this.setProperties({ - '_allBackups': _allBackups, '_allVolumes': _allVolumes, }); }, @@ -58,14 +50,14 @@ var Snapshot = Resource.extend({ }); }, - restoreFromBackup() { - this.get('volume').doAction('restorefrombackup', { - backupId: this.get('latestCompleteBackup.id'), - }); - }, + //restoreFromBackup() { + //this.get('volume').doAction('restorefrombackup', { + //backupId: this.get('latestCompleteBackup.id'), + //}); + //}, deleteBackup() { - this.get('latestBackup').doAction('remove'); + this.doAction('removebackup'); }, }, @@ -73,44 +65,29 @@ var Snapshot = Resource.extend({ return this.get('_allVolumes').filterBy('id', this.get('volumeId'))[0]; }.property('_allVolumes.@each.volumeId','id'), - backups: function() { - return this.get('_allBackups').filterBy('snapshotId', this.get('id')); - }.property('_allBackups.@each.snapshotId','id'), - - latestBackup: function() { - return this.get('backups').sortBy('id').pop(); - }.property('backups.@each.id'), - - latestCompleteBackup: function() { - return this.get('backups').filterBy('state','created').sortBy('id').pop(); - }.property('backups.@each.{id,state}'), - - backupCount: Ember.computed.alias('backups.length'), - hasBackups: Ember.computed.gte('backupCount',1), - backupEnabled: Ember.computed.equal('backupCount', 0), + hasBackups: Ember.computed.notEmpty('backupTargetId'), + backupEnabled: Ember.computed.empty('backupTargetId'), availableActions: function() { var a = this.get('actionLinks'); var volA = this.get('volume.actionLinks'); - let created = this.get('state') === 'created'; - let backedup = !!this.get('latestCompleteBackup'); + let created = this.get('state') === 'snapshotted'; return [ { label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete' }, { divider: true }, { label: 'action.revertToSnapshot', icon: 'icon icon-history', action: 'revertToSnapshot', enabled: created && volA && !!volA.reverttosnapshot }, - { label: 'action.restoreFromBackup', icon: 'icon icon-history', action: 'restoreFromBackup', enabled: created && volA && backedup && !!volA.restorefrombackup }, + { label: 'action.restoreFromBackup', icon: 'icon icon-history', action: 'restoreFromBackup', enabled: created && volA && this.get('hasBackups') && !!volA.restorefrombackup }, { label: 'action.backup', icon: 'icon icon-hdd', action: 'backup', enabled: created && this.get('backupEnabled') }, { label: 'action.deleteBackup', icon: 'icon icon-hdd', action: 'deleteBackup', enabled: this.get('hasBackups') }, { label: 'action.viewInApi', icon: 'icon icon-external-link',action: 'goToApi', enabled: true }, ]; - }.property('actionLinks.remove','backupEnabled','hasBackups','latestCompleteBackup','volume.actionLinks.reverttosnapshot','state','volume.state'), + }.property('actionLinks.remove','backupEnabled','hasBackups','volume.actionLinks.reverttosnapshot','state','volume.state'), }); Snapshot.reopenClass({ reset: function() { - _allBackups = null; _allVolumes = null; } }); diff --git a/app/models/volume.js b/app/models/volume.js index 0d133d95c..dd2102a70 100644 --- a/app/models/volume.js +++ b/app/models/volume.js @@ -4,7 +4,6 @@ const { getOwner } = Ember; // !! If you add a new one of these, you need to add it to reset() below too var _allMounts; -var _allBackups; var _allSnapshots; // !! If you add a new one of these, you need to add it to reset() below too @@ -13,12 +12,10 @@ var Volume = Resource.extend({ // !! If you add a new one of these, you need to add it to reset() below too _allMounts: null, - _allBackups: null, _allSnapshots: null, reservedKeys: [ '_allMounts', - '_allBackups', '_allSnapshots', ], @@ -32,11 +29,6 @@ var Volume = Resource.extend({ _allMounts = store.allUnremoved('mount'); } - if ( !_allBackups ) - { - _allBackups = store.allUnremoved('backup'); - } - if ( !_allSnapshots ) { _allSnapshots = store.allUnremoved('snapshot'); @@ -44,7 +36,6 @@ var Volume = Resource.extend({ this.setProperties({ '_allMounts': _allMounts, - '_allBackups': _allBackups, '_allSnapshots': _allSnapshots, }); }, @@ -93,15 +84,6 @@ var Volume = Resource.extend({ }); }.property('mounts.@each.state'), - backups: function() { - return this.get('_allBackups').filterBy('volumeId', this.get('id')); - }.property('_all_allBackups.@each.volumeId','id'), - - activeBackups: function() { - var backups = this.get('backups')||[]; - return backups.filterBy('state','created'); - }.property('backups.@each.state'), - snapshots: function() { return this.get('_allSnapshots').filterBy('volumeId', this.get('id')); }.property('_allSnapshots.@each.volumeId','id'), @@ -110,7 +92,6 @@ var Volume = Resource.extend({ Volume.reopenClass({ reset: function() { _allMounts = null; - _allBackups = null; _allSnapshots = null; }, diff --git a/app/router.js b/app/router.js index e94fda3a3..09814a202 100644 --- a/app/router.js +++ b/app/router.js @@ -17,6 +17,7 @@ Router.map(function() { this.route('logout'); this.route('authenticated', {path: '/'}, function() { + this.route('dummy-dev', {path: '/dev'}); // Settings this.route('settings', {resetNamespace: true}, function() { this.route('projects', {path: '/env'}, function() { @@ -90,6 +91,11 @@ Router.map(function() { this.route('ports'); this.route('volumes'); this.route('labels'); + this.route('commands'); + this.route('networking'); + this.route('healthcheck'); + this.route('scheduling'); + this.route('security'); }); }); @@ -116,7 +122,6 @@ Router.map(function() { this.route('storagepools', {resetNamespace: true}, function() { this.route('index', {path: '/'}); this.route('pools', {path: '/pools'}); - this.route('backups', {path: '/backups'}); this.route('detail', {path: '/:storagepool_id'}); }); this.route('storagepools.new-volume', {path: '/add-volume', resetNamespace: true}); diff --git a/app/storagepools/backups/controller.js b/app/storagepools/backups/controller.js deleted file mode 100644 index 0add7e9cf..000000000 --- a/app/storagepools/backups/controller.js +++ /dev/null @@ -1,14 +0,0 @@ -import Ember from 'ember'; -import Sortable from 'ui/mixins/sortable'; - -export default Ember.Controller.extend(Sortable, { - sortableContent : Ember.computed.alias('model'), - sortBy: 'name', - sorts: { - state : ['stateSort','name','id'], - name : ['name','id'], - volume : ['volume.name','id'], - created : ['created','id'], - }, - -}); diff --git a/app/storagepools/backups/template.hbs b/app/storagepools/backups/template.hbs deleted file mode 100644 index 3afdeb1f9..000000000 --- a/app/storagepools/backups/template.hbs +++ /dev/null @@ -1,36 +0,0 @@ -
    {{t 'containersPage.containerPage.volumesTab.table.header.state'}}{{t 'containersPage.containerPage.volumesTab.table.header.mount'}}{{t 'containersPage.containerPage.volumesTab.table.header.shared'}}{{t 'containersPage.containerPage.volumesTab.table.header.writable'}}{{t 'containersPage.containerPage.volumesTab.table.header.name'}}{{t 'containersPage.containerPage.volumesTab.table.header.mount'}}{{t 'containersPage.containerPage.volumesTab.table.header.snapshot'}}{{t 'containersPage.containerPage.volumesTab.table.header.actions'}}
    - - {{mount.displayState}} - + + {{mount.ownedVolume.name}} - {{mount.path}} {{copy-to-clipboard size='small' clipboardText=mount.path}} + {{mount.path}} {{if mount.isReadWrite '' '(ro)'}} {{copy-to-clipboard size='small' clipboardText=mount.path}} -
      - {{#each mount.sharedContainers as |container|}} -
    • - {{#link-to "container" container.id}}{{container.displayName}}{{/link-to}} -
    • - {{else}} -
    • {{t 'containersPage.containerPage.volumesTab.table.body.noContainers'}}
    • - {{/each}} -
    +
    + {{snapshot-timeline model=mount.snapshotsAndBackups}} - + + {{action-menu model=mount.ownedVolume}}
    - - - {{sortable-th sortable=this action="changeSort" name="state" width="125" label="generic.state"}} - {{sortable-th sortable=this action="changeSort" name="name" label="generic.name"}} - {{sortable-th sortable=this action="changeSort" name="volumeId" label="storagePoolsPage.volume"}} - {{sortable-th sortable=this action="changeSort" name="created" label="generic.created"}} - - - - - {{#each arranged as |target|}} - - - - - - - - {{/each}} - -
     
    - {{badge-state model=target}} - - {{target.name}} - - {{#if target.volume}} - {{target.volume.displayName}} ({{target.volumeId}}) - {{else}} - {{t 'storagePoolsPage.orphaned'}} - {{/if}} - - {{date-str target.created}} - - {{action-menu model=target}} -
    diff --git a/app/storagepools/route.js b/app/storagepools/route.js index 3c2ddd61e..b8f0cae7e 100644 --- a/app/storagepools/route.js +++ b/app/storagepools/route.js @@ -6,8 +6,6 @@ export default Ember.Route.extend({ return Ember.RSVP.hash({ pools: store.findAllUnremoved('storagepool'), mounts: store.findAllUnremoved('mounts'), - snapshots: store.findAllUnremoved('snapshots'), - backups: store.findAllUnremoved('backups'), }).then((hash) => { return hash.pools.filter((pool) => { return !!pool.get('driverName'); diff --git a/app/storagepools/template.hbs b/app/storagepools/template.hbs index 7752e8d72..7e033a625 100644 --- a/app/storagepools/template.hbs +++ b/app/storagepools/template.hbs @@ -2,10 +2,6 @@

    {{t 'storagePoolsPage.header'}}

    - diff --git a/app/styles/app-dark.scss b/app/styles/app-dark.scss index fd3ad4001..bd69d7745 100644 --- a/app/styles/app-dark.scss +++ b/app/styles/app-dark.scss @@ -62,6 +62,7 @@ @import "app/styles/components/multi-stats"; @import "app/styles/components/tooltip"; @import "app/styles/components/container-shell"; +@import "app/styles/components/timeline"; //****************************************** // Pages diff --git a/app/styles/app-light.scss b/app/styles/app-light.scss index 74bc0e92e..ca4a3466d 100644 --- a/app/styles/app-light.scss +++ b/app/styles/app-light.scss @@ -62,6 +62,7 @@ @import "app/styles/components/multi-stats"; @import "app/styles/components/tooltip"; @import "app/styles/components/container-shell"; +@import "app/styles/components/timeline"; //****************************************** // Pages diff --git a/app/styles/components/_timeline.scss b/app/styles/components/_timeline.scss new file mode 100644 index 000000000..029b332bf --- /dev/null +++ b/app/styles/components/_timeline.scss @@ -0,0 +1,65 @@ +$timeline-height: 15px; + +@mixin timeline-dot { + border-radius: 50%; + border: 1px solid white; + height: $timeline-height; + position: absolute; + top: 0; + width: $timeline-height; + z-index: 2; +} + +.timeline-container { + background-color: $lightGray; + height: $timeline-height; + margin: 0 auto; + position: relative; + width: calc(100% - #{$timeline-height}); + + &:before, + &:after { + background-color: $lightGray; + border-radius: 50%; + content: ''; + display: inline-block; + height: $timeline-height; + position: absolute; + top: 0; + width: $timeline-height; + } + + &:before { + left: calc( calc(#{$timeline-height}/2) * -1 ); + } + + &:after { + right: calc( calc(#{$timeline-height}/2) * -1 ); + } + + .timeline-child { + height: $timeline-height; + width: $timeline-height; + } + + .snapshot { + @include timeline-dot; + + background: $orangeDark; + + &.backedup { + background: $navyTwoDark; + } + + &:first-child { + left: -10px !important; + } + + i.icon { + font-size: 11px; + left: 1px; + position: relative; + top: -3px; + } + } +} diff --git a/app/styles/layout/_layout.scss b/app/styles/layout/_layout.scss index 2768e46cb..4b0b5ff28 100644 --- a/app/styles/layout/_layout.scss +++ b/app/styles/layout/_layout.scss @@ -373,6 +373,9 @@ TABLE.graphs { .inline-block { display : inline-block; } +.block { + display: block; +} .no-select { -webkit-touch-callout : none; diff --git a/app/styles/layout/_md-screen.scss b/app/styles/layout/_md-screen.scss index 549644e57..820bca838 100644 --- a/app/styles/layout/_md-screen.scss +++ b/app/styles/layout/_md-screen.scss @@ -14,7 +14,22 @@ } @media (max-width: $screen-md-max) { + .nav-tabs { + LI { + margin-right: 2px; + + A { + font-size: 13px; + padding: 10px; + + .icon { + display: none; + } + } + } + } + HEADER NAV .navbar-nav > LI { width: auto; } -} \ No newline at end of file +} diff --git a/app/styles/layout/_sm-screen.scss b/app/styles/layout/_sm-screen.scss index bdb9047e6..0ff5d63fa 100644 --- a/app/styles/layout/_sm-screen.scss +++ b/app/styles/layout/_sm-screen.scss @@ -1,16 +1,18 @@ /*xs*/ -@media (min-width: $screen-xs) { // 480px +@media (min-width: $screen-xs) { + // 480px div.banner { - padding: 0 ; + padding: 0; .banner-head { display: block; position: relative; &::after { - display:none; + display: none; } } + .banner-content { display: block; margin-left: 0; @@ -23,10 +25,9 @@ } } - /*small*/ @media (min-width: $screen-sm) { -.col-sm-height { + .col-sm-height { display: table-cell; float: none !important; } @@ -43,7 +44,7 @@ position: absolute; &::after { - display:initial; + display: initial; } } @@ -74,19 +75,24 @@ width: 20%; margin: 0; border-top: dotted 1px $lightGray; + &.count { width: 25%; } + &.stack-actions { width: 11%; } + &.stack-template { width: 50%; } } + .header-left.name { width: 70%; } + .stack-actions { text-align: center !important; } @@ -94,85 +100,86 @@ /*responsive tables and nav 695-959*/ - table, - thead, - tbody, - th, - td, - tr { - display: block; - } + table, + thead, + tbody, + th, + td, + tr { + display: block; + } - thead { - height: 0; + thead { + height: 0; + position: absolute; + top: -9999px; + left: -9999px; + + & tr { position: absolute; top: -9999px; left: -9999px; - - & tr { - position: absolute; - top: -9999px; - left: -9999px; - } } + } - tr { + tr { border: 1px solid $table-border-color; margin: 10px 0; - &.sm-noborder{ + &.sm-noborder { border-width: 0px; } } - td { - border: none; - border-bottom: 1px solid #eee; - position: relative; - padding-left: 50%; - white-space: normal; - text-align:left; - width: 100% !important; - border-right: none !important; + td { + border: none; + border-bottom: 1px solid #eee; + position: relative; + padding-left: 50%; + white-space: normal; + text-align: left; + width: 100% !important; + border-right: none !important; - & .spark-line { - width: 44px !important; - } + & .spark-line { + width: 44px !important; } + } - td:before { - content: attr(data-title); - width: 45%; - margin-right: 10px; - white-space: nowrap; - text-align:left; - font-weight: bold; - } + td:before { + content: attr(data-title); + width: 45%; + margin-right: 10px; + white-space: nowrap; + text-align: left; + font-weight: bold; + } - .stack-section .grid td.state { - padding: 10px !important; - } + .stack-section .grid td.state { + padding: 10px !important; + } .grid TD.actions { - position: static; - text-align: left; - } + position: static; + text-align: left; + } - .stack-section { - background: transparent !important; - } - .stack-section .grid TD, .stack-section .grid TH { - border-bottom: 1px dotted $table-border-color !important; - border-top: none; - text-align: left !important; - } + .stack-section { + background: transparent !important; + } - .grid > thead > tr > th, - .grid > thead > tr > td, - .grid > tbody > tr > th, - .grid > tbody > tr > td, - .grid > tfoot > tr > th, - .grid > tfoot > tr > td { - border-top: none; - } - } \ No newline at end of file + .stack-section .grid TD, .stack-section .grid TH { + border-bottom: 1px dotted $table-border-color !important; + border-top: none; + text-align: left !important; + } + + .grid > thead > tr > th, + .grid > thead > tr > td, + .grid > tbody > tr > th, + .grid > tbody > tr > td, + .grid > tfoot > tr > th, + .grid > tfoot > tr > td { + border-top: none; + } +} diff --git a/app/styles/pages/_loading.scss b/app/styles/pages/_loading.scss index 4b5a7a5ba..08d597b42 100644 --- a/app/styles/pages/_loading.scss +++ b/app/styles/pages/_loading.scss @@ -21,6 +21,15 @@ animation: orbit 20s linear infinite; /* IE 10+, Fx 29+ */ } +.i-hate-spinners { + .orbit, + .loadfield, + .icon-spin { + animation: none; + -webkit-animation:: none; + } +} + .sun { background : $yellowDark; top : 110px; @@ -341,4 +350,4 @@ opacity: 1; filter: alpha(opacity=100); } -} \ No newline at end of file +} diff --git a/app/templates/tooltip-action-menu.hbs b/app/templates/tooltip-action-menu.hbs new file mode 100644 index 000000000..e79f6edf9 --- /dev/null +++ b/app/templates/tooltip-action-menu.hbs @@ -0,0 +1,5 @@ +
    + {{action-menu model=model showPrimary=false inTooltip=true class="pull-right tooltip-more-actions"}} +
    {{displayName}}
    +
    {{format-ip ip=model.displayIp}}
    +
    diff --git a/app/templates/tooltip-snapshot-timeline.hbs b/app/templates/tooltip-snapshot-timeline.hbs new file mode 100644 index 000000000..4782415c6 --- /dev/null +++ b/app/templates/tooltip-snapshot-timeline.hbs @@ -0,0 +1,9 @@ +
    + {{action-menu model=model showPrimary=false inTooltip=true class="pull-right tooltip-more-actions"}} +
    {{displayName}}
    +
    + {{model.state}} + {{format-date model.created}} +
    + +
    diff --git a/app/utils/constants.js b/app/utils/constants.js index 1470553de..1d3315dd8 100644 --- a/app/utils/constants.js +++ b/app/utils/constants.js @@ -132,7 +132,8 @@ var C = { EXPANDED_STACKS : 'expandedStacks', SORT_STACKS_BY : 'sortStacksBy', THEME : 'theme', - LANGUAGE : 'language' + LANGUAGE : 'language', + I_HATE_SPINNERS: 'ihatespinners' }, LANGUAGE: { diff --git a/tests/unit/storagepools/backups/route-test.js b/tests/unit/storagepools/backups/route-test.js deleted file mode 100644 index 8499bf418..000000000 --- a/tests/unit/storagepools/backups/route-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { moduleFor, test } from 'ember-qunit'; - -moduleFor('route:storagepools/backups', 'Unit | Route | storagepools/backups', { - // Specify the other units that are required for this test. - // needs: ['controller:foo'] -}); - -test('it exists', function(assert) { - let route = this.subject(); - assert.ok(route); -}); diff --git a/translations/en-us.yaml b/translations/en-us.yaml index 4d50a01b8..e1dac54b3 100644 --- a/translations/en-us.yaml +++ b/translations/en-us.yaml @@ -408,6 +408,11 @@ containersPage: labels: Labels volumes: Volumes ports: Ports + command: Command + networking: Networking + healthCheck: Health Check + scheduling: Scheduling + security: Security portsTab: table: header: @@ -426,15 +431,21 @@ containersPage: volumesTab: table: header: + name: Name state: State mount: Mount Point shared: Shared With writable: Writable + snapshot: Snapshot Timeline + actions: Actions body: + name: 'Name:' state: 'State:' mount: 'Mount Point:' shared: 'Shared With:' writable: 'Writable:' + snapshot: 'Snapshot Timeline:' + actions: 'Actions:' error: data: 'Error:' error: Error @@ -1009,7 +1020,7 @@ stacksPage: label: Start services after creating storagePoolsPage: - header: Storage + header: Storage Pools volume: Volume Id orphaned: Orphaned Backup nav: @@ -1720,14 +1731,22 @@ formNameDescription: placeholder: Description formNetwork: + links: + table: + dest: Destination Container + as: As Name + data: + dest: 'Destination Container:' + as: 'As Name:' networkMode: label: Network bridge: Bridge + container: container host: Host managed: Managed none: None container: - label: ontainer + label: container requestedIp: label: Requested IP placeholder: e.g. 10.42.2.24 @@ -1804,6 +1823,8 @@ formScheduling: formSecurity: + key: Key + value: Value logConfig: addActionLabel: Add Option keyPlaceholder: e.g. syslog-facility @@ -1849,7 +1870,7 @@ formSecurity: label: Log Driver placeholder: e.g. syslog logOptions: - label: Log + label: Log Options formServiceLinks: addAction: Service Links @@ -2687,6 +2708,9 @@ siteAccess: groups: Groups organizations: Organizations +snapshotTimeline: + noData: No Snapshot Data + stackSection: outputs: Outputs description: Description diff --git a/vendor/icons b/vendor/icons index 1d0df61b4..1f650aae6 160000 --- a/vendor/icons +++ b/vendor/icons @@ -1 +1 @@ -Subproject commit 1d0df61b40b00f425ed65c6be7619fee7fd6f11b +Subproject commit 1f650aae61ed57c61c1d8defd0797f83f80ea9e8