API Docs for:
Show:

File: app/views/unit.js

'use strict';

/**
 * The unit view(s).
 *
 * @module views
 * @submodule views.unit
 */

YUI.add('juju-view-unit', function(Y) {

  var views = Y.namespace('juju.views'),
      models = Y.namespace('juju.models'),
      utils = Y.namespace('juju.views.utils'),
      Templates = views.Templates;

  /**
   * Display a unit.
   *
   * @class UnitView
   */
  var UnitView = Y.Base.create('UnitView', Y.View, [], {
    template: Templates.unit,

    /**
     * Render the view.
     *
     * @method render
     * @chainable
     */
    render: function() {
      var container = this.get('container');
      var unit = this.get('unit');
      if (!unit) {
        container.setHTML('<div class="alert">Loading...</div>');
        console.log('waiting on unit data');
        return this;
      }

      var db = this.get('db'),
          service = db.services.getById(unit.service),
          env = db.environment.get('annotations');

      if (!service.get('loaded')) {
        container.setHTML('<div class="alert">Loading...</div>');
        console.log('waiting on service data');
        return this;
      }

      var charm = db.charms.getById(service.get('charm'));

      if (!charm) {
        container.setHTML('<div class="alert">Loading...</div>');
        console.log('waiting on charm data');
        return this;
      }

      UnitView.superclass.render.apply(this, arguments);

      var ip_description_chunks = [];
      if (unit.public_address) {
        ip_description_chunks.push(unit.public_address);
      }
      if (unit.private_address) {
        ip_description_chunks.push(unit.private_address);
      }
      if (unit.open_ports) {
        ip_description_chunks.push(unit.open_ports.join());
      }
      var unit_ip_description;
      if (ip_description_chunks.length) {
        unit_ip_description = ip_description_chunks.join(' | ');
      }

      var state = utils.simplifyState(unit, true); // Ignore relations errors.

      var relation_errors = unit.relation_errors || {},
          relations = utils.getRelationDataForService(db, service),
          querystring = this.get('querystring');

      Y.each(relations, function(rel) {
        // relation_errors example: {'website': ['haproxy'], 'db': ['mysql']}
        var match = relation_errors[rel.near.name],
            far = rel.far || rel.near;
        rel.has_error = !!(match && match.indexOf(far.service) > -1);
        rel.highlight = !!(
            querystring.rel_id && querystring.rel_id === rel.elementId);
      });

      var charmAttrs = charm.getAttrs();
      var nsRouter = this.get('nsRouter');

      container.setHTML(this.template({
        charmUri: nsRouter.url({
          gui: '/charms/charms/' + charmAttrs.series +
              '/' + charmAttrs.package_name + '/json'
        }),
        serviceRootUri: nsRouter.url({
          gui: '/service/'
        }),
        unit: unit,
        unit_ip_description: unit_ip_description,
        landscape: this.get('landscape'),
        service: service.getAttrs(),
        disabled_remove: service.get('unit_count') <= 1,
        charm: charmAttrs,
        machine: db.machines.getById(unit.machine),
        hasErrors: state === 'error',
        isRunning: state === 'running',
        isPending: state === 'pending',
        relations: relations}));
      views.utils.updateLandscapeBottomBar(this.get('landscape'), env, unit,
          container);
      return this;
    },

    events: {
      '#resolved-unit-button': {click: 'confirmResolved'},
      '#retry-unit-button': {click: 'retry'},
      '#remove-unit-button': {click: 'confirmRemoved'},
      '.resolved-relation-button': {click: 'confirmResolvedRelation'},
      '.retry-relation-button': {click: 'retryRelation'}
    },

    confirmResolved: function(ev) {
      // We wait to make the panel until now, because in the render method
      // the container is not yet part of the document.
      if (Y.Lang.isUndefined(this.resolved_panel)) {
        this.resolved_panel = views.createModalPanel(
            'Are you sure you want to tell the system this problem has been ' +
            'resolved?  This action cannot be undone.',
            '#resolved-modal-panel',
            'Error Has Been Resolved',
            Y.bind(this.doResolvedUnit, this));
      }
      this.resolved_panel.show();
    },

    doResolvedUnit: function(ev) {
      ev.preventDefault();
      var env = this.get('env'),
          unit = this.get('unit');
      ev.target.set('disabled', true);
      env.resolved(unit.id, null, false,
                     Y.bind(this._resolvedUnitCallback, this, ev.target));
    },

    _resolvedUnitCallback: function(button, ev) {
      var unit = this.get('unit'),
          db = this.get('db'),
          getModelURL = this.get('getModelURL'),
          service = db.services.getById(unit.service);
      if (ev.err) {
        db.notifications.add(
            new models.Notification({
              title: 'Error resolving unit',
              message: 'Unit name: ' + ev.unit_name,
              level: 'error',
              link: getModelURL(unit),
              modelId: unit
            })
        );
      } else {
        this.resolved_panel.hide();
      }
      button.set('disabled', false);
    },

    confirmRemoved: function(ev) {
      // We wait to make the panel until now, because in the render method
      // the container is not yet part of the document.
      ev.preventDefault();
      var unit = this.get('unit'),
          service = this.get('db').services.getById(unit.service);
      if (Y.Lang.isUndefined(this.remove_panel)) {
        this.remove_panel = views.createModalPanel(
            'Are you sure you want to remove this unit?  ' +
                'This action cannot be undone, though you can ' +
                'simply add another unit later.',
            '#remove-modal-panel',
            'Remove Unit',
            Y.bind(this.doRemoveUnit, this));
      }
      this.remove_panel.show();
    },

    doRemoveUnit: function(ev) {
      ev.preventDefault();
      var env = this.get('env'),
          unit = this.get('unit');
      ev.target.set('disabled', true);
      env.remove_units(
          [unit.id],
          Y.bind(this._removeUnitCallback, this, ev.target));
    },

    _removeUnitCallback: function(btn, ev) {
      var unit = this.get('unit'),
          db = this.get('db'),
          getModelURL = this.get('getModelURL'),
          service = db.services.getById(unit.service),
          unit_name = ev.unit_names[0];

      if (ev.err) {
        db.notifications.add(
            new models.Notification({
              title: 'Error removing unit',
              message: 'Unit name: ' + unit_name,
              level: 'error',
              link: getModelURL(unit),
              modelId: unit
            })
        );
      } else {
        db.units.remove(db.units.getById(unit_name));
        service.set('unit_count', service.get('unit_count') - 1);
        this.remove_panel.destroy();
        this.fire('navigateTo',
            {url: this.get('nsRouter').url(
                {gui: '/service/' + service.get('displayName') + '/'})});
      }
      btn.set('disabled', false);
    },

    retry: function(ev) {
      ev.preventDefault();
      var env = this.get('env'),
          unit = this.get('unit'),
          db = this.get('db'),
          getModelURL = this.get('getModelURL'),
          service = db.services.getById(unit.service),
          button = ev.target;
      button.set('disabled', true);
      env.resolved(unit.id, null, true,
          function(ev) {
            if (ev.err) {
              db.notifications.add(
                  new models.Notification({
                    title: 'Error retrying unit',
                    message: 'Unit name: ' + ev.unit_name,
                    level: 'error',
                    link: getModelURL(unit),
                    modelId: unit
                  })
              );

            }
            button.set('disabled', false);
          }
      );
    },

    confirmResolvedRelation: function(ev) {
      // We wait to make the panel until now, because in the render method
      // the container is not yet part of the document.
      if (Y.Lang.isUndefined(this.resolved_relation_panel)) {
        this.resolved_relation_panel = views.createModalPanel(
            'Are you sure you want to tell the system this problem has been ' +
            'resolved?  This action cannot be undone.',
            '#resolved-relation-modal-panel');
      }
      // We set the buttons separately every time because we want to bind the
      // target, which can vary.
      views.setModalButtons(
          this.resolved_relation_panel,
          'Relation Error Has Been Resolved',
          Y.bind(this.doResolvedRelation, this, ev.target));
      this.resolved_relation_panel.show();
    },

    doResolvedRelation: function(button, ev) {
      ev.preventDefault();
      var env = this.get('env'),
          unit = this.get('unit'),
          relation_name = button.ancestor('form').get('id');
      ev.target.set('disabled', true);
      env.resolved(
          unit.id, relation_name, false,
          Y.bind(this._resolvedRelationCallback, this, button, ev.target));
    },

    _resolvedRelationCallback: function(button, confirm_button, ev) {
      var row = button.ancestor('tr');
      views.highlightRow(row, ev.err);
      if (ev.err) {
        var db = this.get('db'),
            getModelURL = this.get('getModelURL'),
            unit = this.get('unit'),
            relation_id = button.get('value'),
            relation = db.relations.getById(relation_id),
            elementId = row.get('id');
        db.notifications.add(
            new models.Notification({
              title: 'Error resolving relation',
              message: 'Could not resolve a unit relation',
              level: 'error',
              link: getModelURL(unit) + '?rel_id=' + elementId,
              modelId: relation
            })
        );
      }
      confirm_button.set('disabled', false);
      this.resolved_relation_panel.hide();
    },

    retryRelation: function(ev) {
      ev.preventDefault();
      var env = this.get('env'),
          unit = this.get('unit'),
          db = this.get('db'),
          getModelURL = this.get('getModelURL'),
          button = ev.target,
          relation_name = button.ancestor('form').get('id');
      button.set('disabled', true);
      env.resolved(
          unit.id, relation_name, true,
          function(ev) {
            var row = button.ancestor('tr');
            views.highlightRow(row, ev.err);
            if (ev.err) {
              var relation_id = button.get('value'),
                  relation = db.relations.getById(relation_id),
                  elementId = row.get('id');
              db.notifications.add(
                  new models.Notification({
                    title: 'Error retrying relation',
                    message: 'Could not retry a unit relation',
                    level: 'error',
                    link: getModelURL(unit) + '?rel_id=' + elementId,
                    modelId: relation
                  })
              );
            }
            button.set('disabled', false);
          }
      );
    }

  }, {
    ATTRS: {
      /**
        Applications router utility methods

        @attribute nsRouter
      */
      nsRouter: {}
    }
  });

  views.unit = UnitView;

}, '0.1.0', {
  requires: [
    'd3',
    'base',
    'handlebars',
    'node',
    'juju-view-utils',
    'view']});