ReplicaList.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import React, { PropTypes } from 'react';
  15. import Reflux from 'reflux';
  16. import withStyles from 'isomorphic-style-loader/lib/withStyles';
  17. import Location from '../../core/Location';
  18. import UserIcon from '../UserIcon';
  19. import NotificationIcon from '../NotificationIcon';
  20. import Moment from 'react-moment';
  21. import s from './ReplicaList.scss';
  22. import MigrationStore from '../../stores/MigrationStore';
  23. import MigrationActions from '../../actions/MigrationActions';
  24. import TextTruncate from 'react-text-truncate';
  25. import ProjectsDropdown from '../ProjectsDropdown';
  26. import MainList from '../MainList';
  27. import Helper from '../Helper';
  28. const title = 'Coriolis Replicas';
  29. const filters = [
  30. {
  31. field: "status",
  32. options: [
  33. { value: null, label: "All" },
  34. { value: "RUNNING", label: "Running" },
  35. { value: "ERROR", label: "Error" },
  36. { value: "COMPLETED", label: "Completed" }
  37. ]
  38. }
  39. ]
  40. const replicaActions = {
  41. execute_action: {
  42. label: "Execute",
  43. action: (item) => {
  44. MigrationActions.executeReplica(item)
  45. },
  46. confirm: false
  47. },
  48. delete_action: {
  49. label: "Delete",
  50. action: (item) => {
  51. MigrationActions.deleteReplica(item)
  52. },
  53. confirm: true
  54. }
  55. }
  56. class ReplicaList extends Reflux.Component {
  57. constructor(props) {
  58. super(props)
  59. this.store = MigrationStore;
  60. this.state = {
  61. replicas: null
  62. }
  63. this.renderItem = this.renderItem.bind(this)
  64. }
  65. static contextTypes = {
  66. onSetTitle: PropTypes.func.isRequired
  67. };
  68. componentWillMount() {
  69. super.componentWillMount.call(this)
  70. this.context.onSetTitle(title);
  71. MigrationActions.loadReplicas()
  72. }
  73. newMigration() {
  74. Location.push('/replicas/new')
  75. }
  76. replicaDetail(e, item) {
  77. Location.push('/replica/' + item.id + "/")
  78. }
  79. renderItem(item) {
  80. let count = 0
  81. if (item.executions.length) {
  82. item.tasks = item.executions[item.executions.length - 1].tasks
  83. }
  84. if (!item.tasks) {
  85. item.tasks = []
  86. }
  87. item.tasks.forEach((task) => {
  88. if (task.status != "COMPLETED") count++
  89. })
  90. let tasksRemaining = count + " out of " + item.tasks.length
  91. if (count == 0) {
  92. tasksRemaining = "-"
  93. }
  94. return (
  95. <div className={"item " + (item.selected ? "selected" : "")} key={"replica_" + item.id}>
  96. <span className="cell cell-icon" onClick={(e) => this.replicaDetail(e, item)}>
  97. <div className={"icon " + item.type}></div>
  98. <span className="details">
  99. <TextTruncate line={1} truncateText="..." text={item.name} />
  100. <span className={s.migrationStatus + " status-pill " + item.status}>{item.status}</span>
  101. </span>
  102. </span>
  103. <span className="cell" onClick={(e) => this.replicaDetail(e, item)}>
  104. <div className={s.cloudImage + " icon small-cloud " + item.origin_endpoint_type}></div>
  105. <span className={s.chevronRight}></span>
  106. <div className={s.cloudImage + " icon small-cloud " + item.destination_endpoint_type}></div>
  107. </span>
  108. <span className={"cell " + s.composite} onClick={(e) => this.replicaDetail(e, item)}>
  109. <span className={s.label}>Last execution</span>
  110. <span className={s.value}>
  111. {
  112. item.lastExecution ?
  113. <Moment format="MMM Do YYYY HH:mm" date={Helper.getTimeObject(item.lastExecution)} /> : "-"
  114. }
  115. </span>
  116. </span>
  117. <span className={"cell " + s.composite} onClick={(e) => this.replicaDetail(e, item)}>
  118. <span className={s.label}>Tasks remaining</span>
  119. <span className={s.value}>{tasksRemaining}</span>
  120. </span>
  121. </div>
  122. )
  123. }
  124. currentInstance() {
  125. return "N/A"
  126. }
  127. refreshList() {
  128. MigrationActions.loadReplicas()
  129. }
  130. render() {
  131. if ((this.state.replicas && this.state.replicas.length) || this.state.replicas == null) {
  132. return (
  133. <div className={s.root}>
  134. <div className={s.container}>
  135. <div className={s.pageHeader}>
  136. <div className={s.top}>
  137. <h1>{title}</h1>
  138. <div className={s.topActions}>
  139. <ProjectsDropdown />
  140. <button onClick={this.newMigration}>New</button>
  141. <UserIcon />
  142. <NotificationIcon />
  143. </div>
  144. </div>
  145. </div>
  146. <MainList
  147. items={this.state.replicas}
  148. actions={replicaActions}
  149. itemName="replica"
  150. renderItem={this.renderItem}
  151. filters={filters}
  152. refresh={this.refreshList}
  153. />
  154. </div>
  155. </div>
  156. );
  157. } else {
  158. return (
  159. <div className={s.root}>
  160. <div className={s.container}>
  161. <div className={s.pageHeader}>
  162. <div className={s.top}>
  163. <h1>{title}</h1>
  164. <div className={s.topActions}>
  165. <ProjectsDropdown />
  166. <button onClick={this.newMigration}>New</button>
  167. <UserIcon />
  168. <NotificationIcon />
  169. </div>
  170. </div>
  171. <div className="noResultsLarge">
  172. <span className="icon"></span>
  173. <h3>It seems that you don't have any Replica's in this project</h3>
  174. <p>The Coriolis Replica is obtained by replicating incrementally the<br />
  175. virtual machines data from the source cloud endpoint to the target.
  176. </p>
  177. <button onClick={(e) => this.newMigration(e)}>Create a Replica</button>
  178. </div>
  179. </div>
  180. </div>
  181. </div>
  182. );
  183. }
  184. }
  185. }
  186. export default withStyles(ReplicaList, s);