mirror of https://github.com/rancher/ui.git
349 lines
8.9 KiB
JavaScript
349 lines
8.9 KiB
JavaScript
import { computed, get, set } from '@ember/object';
|
|
import { or, alias } from '@ember/object/computed';
|
|
import Resource from '@rancher/ember-api-store/models/resource';
|
|
import { download } from 'shared/utils/util';
|
|
import C from 'ui/utils/constants';
|
|
import StateCounts from 'ui/mixins/state-counts';
|
|
import { inject as service } from '@ember/service';
|
|
import { hasMany, reference } from '@rancher/ember-api-store/utils/denormalize';
|
|
import ResourceUsage from 'shared/mixins/resource-usage';
|
|
import Grafana from 'shared/mixins/grafana';
|
|
import { next } from '@ember/runloop';
|
|
|
|
const UNSCHEDULABLE_KEYS = ['node-role.kubernetes.io/etcd', 'node-role.kubernetes.io/controlplane'];
|
|
const UNSCHEDULABLE_EFFECTS = ['NoExecute', 'NoSchedule'];
|
|
|
|
const CONTAINERD = 'containerd://';
|
|
|
|
var Node = Resource.extend(Grafana, StateCounts, ResourceUsage, {
|
|
modalService: service('modal'),
|
|
settings: service(),
|
|
prefs: service(),
|
|
router: service(),
|
|
globalStore: service(),
|
|
clusterStore: service(),
|
|
intl: service(),
|
|
|
|
nodes: hasMany('clusterId', 'node', 'clusterId'),
|
|
type: 'node',
|
|
containerD: CONTAINERD,
|
|
isContainerD: false,
|
|
|
|
grafanaDashboardName: 'Nodes',
|
|
grafanaResourceId: alias('ipAddress'),
|
|
|
|
cluster: reference('clusterId', 'cluster'),
|
|
nodePool: reference('nodePoolId'),
|
|
|
|
displayIp: or('externalIpAddress', 'ipAddress'),
|
|
|
|
init() {
|
|
this._super(...arguments);
|
|
this.defineStateCounts('arrangedInstances', 'instanceStates', 'instanceCountSort');
|
|
},
|
|
|
|
availableActions: computed('actionLinks.{cordon,drain,uncordon}', 'canScaleDown', 'links.nodeConfig', function() {
|
|
let l = this.links;
|
|
const a = this.actionLinks || {};
|
|
|
|
let out = [
|
|
{
|
|
label: 'action.cordon',
|
|
icon: 'icon icon-pause',
|
|
action: 'cordon',
|
|
enabled: !!a.cordon,
|
|
bulkable: true
|
|
},
|
|
{
|
|
label: 'action.uncordon',
|
|
icon: 'icon icon-play',
|
|
action: 'uncordon',
|
|
enabled: !!a.uncordon,
|
|
bulkable: true
|
|
},
|
|
{
|
|
label: 'action.drain',
|
|
icon: 'icon icon-snapshot',
|
|
action: 'drain',
|
|
enabled: !!a.drain,
|
|
bulkable: true
|
|
},
|
|
{
|
|
label: 'action.scaledown',
|
|
icon: 'icon icon-arrow-circle-up icon-rotate-180',
|
|
action: 'scaledown',
|
|
enabled: this.canScaleDown,
|
|
bulkable: true
|
|
},
|
|
{
|
|
label: 'action.stopDrain',
|
|
icon: 'icon icon-stop',
|
|
action: 'stopDrain',
|
|
enabled: !!a.stopDrain,
|
|
bulkable: true,
|
|
},
|
|
{ divider: true },
|
|
{
|
|
label: 'action.nodeConfig',
|
|
icon: 'icon icon-download',
|
|
action: 'nodeConfig',
|
|
enabled: !!l.nodeConfig
|
|
},
|
|
{ divider: true },
|
|
];
|
|
|
|
return out;
|
|
}),
|
|
|
|
canScaleDown: computed('actionLinks.scaledown', 'nodePool.quantity', function() {
|
|
const actions = this.actionLinks;
|
|
const nodePool = this.nodePool;
|
|
|
|
return !!actions?.scaledown && nodePool?.quantity > 1;
|
|
}),
|
|
|
|
displayName: computed('id', 'name', 'nodeName.length', 'nodes', 'requestedHostname', function() {
|
|
let name = this.name;
|
|
|
|
if ( name ) {
|
|
return name;
|
|
}
|
|
|
|
name = this.nodeName;
|
|
if ( name ) {
|
|
if ( name.match(/[a-z]/i) ) {
|
|
name = this.parseNodeName(name);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
name = this.requestedHostname;
|
|
if ( name ) {
|
|
return name;
|
|
}
|
|
|
|
return `(${ this.id })`;
|
|
}),
|
|
|
|
rolesArray: computed('etcd', 'controlPlane', 'worker', function() {
|
|
return ['etcd', 'controlPlane', 'worker'].filter((x) => !!get(this, x));
|
|
}),
|
|
|
|
displayRoles: computed('intl.locale', 'rolesArray.[]', function() {
|
|
const intl = this.intl;
|
|
const roles = this.rolesArray;
|
|
|
|
if ( roles.length >= 3 ) {
|
|
return [intl.t('generic.all')];
|
|
}
|
|
|
|
return roles.map((role) => {
|
|
let key = `model.machine.role.${ role }`;
|
|
|
|
if ( intl.exists(key) ) {
|
|
return intl.t(key);
|
|
}
|
|
|
|
return key;
|
|
});
|
|
}),
|
|
|
|
sortRole: computed('rolesArray.[]', function() {
|
|
let roles = this.rolesArray;
|
|
|
|
if ( roles.length >= 3 ) {
|
|
return 1;
|
|
}
|
|
|
|
if ( roles.includes('controlPlane') ) {
|
|
return 2;
|
|
}
|
|
|
|
if ( roles.includes('etcd') ) {
|
|
return 3;
|
|
}
|
|
|
|
return 4;
|
|
}),
|
|
|
|
isUnschedulable: computed('taints.@each.{effect,key}', function(){
|
|
const taints = this.taints || [];
|
|
|
|
return taints.some((taint) => UNSCHEDULABLE_KEYS.includes(taint.key) && UNSCHEDULABLE_EFFECTS.includes(taint.effect));
|
|
}),
|
|
|
|
isK3sNode: computed('labels', function() {
|
|
const labels = this.labels || {};
|
|
|
|
return Object.prototype.hasOwnProperty.call(labels, C.LABEL.NODE_INSTANCE_TYPE);
|
|
}),
|
|
|
|
k3sNodeArgs: computed('annotations', function() {
|
|
const { annotations } = this;
|
|
const nodeArgs = annotations[C.LABEL.K3S_NODE_ARGS] ? JSON.parse(annotations[C.LABEL.K3S_NODE_ARGS]) : [];
|
|
|
|
return nodeArgs.join(' ');
|
|
}),
|
|
|
|
k3sNodeEnvVar: computed('annotations', function() {
|
|
const { annotations } = this;
|
|
const nodeEnv = annotations[C.LABEL.K3S_NODE_ENV] ? JSON.parse(annotations[C.LABEL.K3S_NODE_ENV]) : {};
|
|
const nodeEnvArr = [];
|
|
|
|
Object.keys(nodeEnv).forEach((envKey) => {
|
|
const out = {
|
|
key: envKey,
|
|
value: nodeEnv[envKey]
|
|
};
|
|
|
|
nodeEnvArr.push(out)
|
|
})
|
|
|
|
return nodeEnvArr;
|
|
}),
|
|
|
|
osBlurb: computed('info.os.operatingSystem', function() {
|
|
var out = get(this, 'info.os.operatingSystem') || '';
|
|
|
|
out = out.replace(/\s+\(.*?\)/, ''); // Remove details in parens
|
|
out = out.replace(/;.*$/, ''); // Or after semicolons
|
|
// RHEL 7 uses 'Red Hat Enterprise Linux Server', RHEL 8 uses 'Red Hat Enterprise Linux'
|
|
out = out.replace(/(Red Hat Enterprise Linux(\sServer|))/, 'RHEL'); // That's kinda long
|
|
|
|
return out;
|
|
}),
|
|
|
|
engineIcon: computed('info.os.dockerVersion', function() {
|
|
if ( (get(this, 'info.os.dockerVersion') || '').startsWith(CONTAINERD) ) {
|
|
return 'icon-container-d';
|
|
}
|
|
|
|
return 'icon-docker';
|
|
}),
|
|
|
|
versionBlurb: computed('info.os.dockerVersion', 'isContainerD', function() {
|
|
let version = get(this, 'info.os.dockerVersion') || '';
|
|
|
|
if ( version.startsWith(CONTAINERD) ) {
|
|
version = version.substr(CONTAINERD.length);
|
|
|
|
if (!this.isContainerD) {
|
|
next(() => set(this, 'isContainerD', true));
|
|
}
|
|
} else {
|
|
if (this.isContainerD) {
|
|
next(() => set(this, 'isContainerD', false));
|
|
}
|
|
}
|
|
|
|
const idx = version.indexOf('+');
|
|
|
|
if ( idx > 0 ) {
|
|
version = version.substr(0, idx);
|
|
}
|
|
|
|
return version;
|
|
}),
|
|
|
|
osInfo: computed('labels', function() {
|
|
const labels = this.labels || {};
|
|
|
|
return labels['beta.kubernetes.io/os'];
|
|
}),
|
|
|
|
// or they will not be pulled in correctly.
|
|
displayEndpoints: computed('publicEndpoints.@each.{ipAddress,port,serviceId,instanceId}', function() {
|
|
var store = this.clusterStore;
|
|
|
|
return (this.publicEndpoints || []).map((endpoint) => {
|
|
if ( !endpoint.service ) {
|
|
endpoint.service = store.getById('service', endpoint.serviceId);
|
|
}
|
|
|
|
endpoint.instance = store.getById('instance', endpoint.instanceId);
|
|
|
|
return endpoint;
|
|
});
|
|
}),
|
|
|
|
// If you use this you must ensure that services and containers are already in the store
|
|
requireAnyLabelStrings: computed(`labels.${ C.LABEL.REQUIRE_ANY }`, 'labels', function() {
|
|
return ((this.labels || {})[C.LABEL.REQUIRE_ANY] || '')
|
|
.split(/\s*,\s*/)
|
|
.filter((x) => x.length > 0 && x !== C.LABEL.SYSTEM_TYPE);
|
|
}),
|
|
parseNodeName(nameIn) {
|
|
const suffix = nameIn.split('.').slice(1).join('.');
|
|
const nodesWithSameSuffix = (this.nodes || []).filter((node) => (node.nodeName || '').endsWith(suffix));
|
|
|
|
if (nodesWithSameSuffix.length === 1) {
|
|
return this.nodeName;
|
|
} else if (nodesWithSameSuffix.length > 1) {
|
|
const neu = nameIn.replace(/\..*$/, '');
|
|
|
|
if ( neu.match(/^\d+$/) ) {
|
|
return this.nodeName;
|
|
} else {
|
|
return neu;
|
|
}
|
|
}
|
|
|
|
return nameIn;
|
|
},
|
|
|
|
actions: {
|
|
activate() {
|
|
return this.doAction('activate');
|
|
},
|
|
|
|
deactivate() {
|
|
return this.doAction('deactivate');
|
|
},
|
|
|
|
cordon() {
|
|
return this.doAction('cordon');
|
|
},
|
|
|
|
uncordon() {
|
|
return this.doAction('uncordon');
|
|
},
|
|
|
|
scaledown() {
|
|
return this.doAction('scaledown');
|
|
},
|
|
|
|
drain() {
|
|
this.modalService.toggleModal('modal-drain-node', {
|
|
escToClose: true,
|
|
resources: [this],
|
|
});
|
|
},
|
|
|
|
stopDrain() {
|
|
return this.doAction('stopDrain');
|
|
},
|
|
|
|
newContainer() {
|
|
this.router.transitionTo('containers.run', { queryParams: { hostId: get(this, 'model.id') } });
|
|
},
|
|
|
|
edit() {
|
|
this.modalService.toggleModal('modal-edit-host', this);
|
|
},
|
|
|
|
nodeConfig() {
|
|
var url = this.linkFor('nodeConfig');
|
|
|
|
if ( url ) {
|
|
download(url);
|
|
}
|
|
}
|
|
},
|
|
|
|
});
|
|
|
|
Node.reopenClass({ defaultSortBy: 'name,hostname', });
|
|
|
|
export default Node;
|