Ver código fonte

Merge commit '0081348e37701a5a2959ab376a81da5ada64ed89' into linted

* commit '0081348e37701a5a2959ab376a81da5ada64ed89':
  - Changed modal styling
  - Removed helptext
  - Fixes issues in wizard flow - Adds validation for options
  - Collects network_date from target_env - Gets networks from VM info

# Conflicts:
#	src/actions/MigrationActions/MigrationActions.js
#	src/components/WizardNetworks/WizardNetworks.js
#	src/components/WizardOptions/WizardOptions.js
#	src/components/WizardTarget/WizardTarget.js
George Vrancianu 8 anos atrás
pai
commit
18f5dceae0

+ 2 - 0
src/actions/ConnectionsActions/ConnectionsActions.js

@@ -25,6 +25,8 @@ let ConnectionsActions = Reflux.createActions({
   loadConnectionDetail: { children: ['completed', 'failed'] },
   loadProviders: { children: ['completed', 'failed'] },
   loadInstances: { children: ['completed', 'failed'] },
+  loadInstanceDetail: { children: ['completed', 'failed'] },
+  loadNetworks: { children: ['completed', 'failed'] },
   newEndpoint: { children: ['success', 'failed'] },
   saveEndpoint: { children: ['success', 'failed'] },
   editEndpoint: { children: ['success', 'failed'] },

+ 1 - 1
src/actions/MigrationActions/MigrationActions.js

@@ -228,7 +228,7 @@ MigrationActions.addMigration.listen((migration) => {
 
   let networkMap = {}
   migration.networks.forEach(network => {
-    networkMap[network.name] = network.migrateNetwork
+    networkMap[network.network_name] = network.migrateNetwork
   })
 
   let destinationEnv = {}

+ 19 - 1
src/components/CloudConnectionsView/CloudConnectionsView.js

@@ -141,22 +141,40 @@ class CloudConnectionsView extends Component {
     let title = "Edit Connection"
 
     let modalStyle = {
+      overlay : {
+        position: "fixed",
+        top: 0,
+        left: 0,
+        right: 0,
+        bottom: 0,
+        backgroundColor:"rgba(164, 170, 181, 0.69)"
+      },
       content: {
         padding: "0px",
+        border: "none",
         borderRadius: "4px",
         bottom: "auto",
         width: "576px",
         height: "auto",
         left: "50%",
-        top: "70px",
+        top: "120px",
         marginLeft: "-288px"
       }
     }
 
     let validationModalStyle = {
+      overlay : {
+          position: "fixed",
+          top: 0,
+          left: 0,
+          right: 0,
+          bottom: 0,
+          backgroundColor:"rgba(164, 170, 181, 0.69)"
+        },
       content: {
         padding: "0px",
         borderRadius: "4px",
+        border: "none",
         bottom: "auto",
         width: "370px",
         height: "250px",

+ 10 - 1
src/components/CloudItem/CloudItem.js

@@ -136,14 +136,23 @@ class CloudItem extends Component {
     }
 
     let modalStyle = {
+      overlay : {
+          position: "fixed",
+          top: 0,
+          left: 0,
+          right: 0,
+          bottom: 0,
+          backgroundColor:"rgba(164, 170, 181, 0.69)"
+        },
       content: {
         padding: "0px",
         borderRadius: "4px",
+        border: "none",
         bottom: "auto",
         width: "576px",
         height: "auto",
         left: "50%",
-        top: "70px",
+        top: "120px",
         marginLeft: "-288px"
       }
     }

+ 9 - 0
src/components/ConfirmationDialog/ConfirmationDialog.js

@@ -48,10 +48,19 @@ class ConfirmationDialog extends Component {
 
   render() {
     let modalStyle = {
+      overlay : {
+          position: "fixed",
+          top: 0,
+          left: 0,
+          right: 0,
+          bottom: 0,
+          backgroundColor:"rgba(164, 170, 181, 0.69)"
+        },
       content: {
         padding: "16px",
         borderRadius: "4px",
         bottom: "auto",
+        border: "none",
         width: "250px",
         height: "auto",
         left: "50%",

+ 10 - 1
src/components/EndpointList/EndpointList.js

@@ -133,14 +133,23 @@ class EndpointList extends Reflux.Component {
 
   render() {
     let modalStyle = {
+      overlay : {
+          position: "fixed",
+          top: 0,
+          left: 0,
+          right: 0,
+          bottom: 0,
+          backgroundColor:"rgba(164, 170, 181, 0.69)"
+        },
       content: {
         padding: "0px",
         borderRadius: "4px",
+        border: "none",
         bottom: "auto",
         width: "576px",
         height: "auto",
         left: "50%",
-        top: "70px",
+        top: "120px",
         marginLeft: "-288px"
       }
     }

+ 1 - 1
src/components/MigrationWizard/MigrationWizard.scss

@@ -27,7 +27,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   .stepTitle {
     font-weight: $weight-light;
     font-size: 32px;
-    color: #0056B8;
+    color: $blue;
     text-align: center;
     background-color: #FFF;
     width: $wizard-content-width;

+ 117 - 57
src/components/WizardNetworks/WizardNetworks.js

@@ -19,7 +19,9 @@ import React, { Component, PropTypes } from 'react';
 import withStyles from 'isomorphic-style-loader/lib/withStyles';
 import s from './WizardNetworks.scss';
 import Dropdown from '../NewDropdown';
-import { targetNetworkMock } from '../../config';
+import WizardActions from '../../actions/WizardActions';
+import LoadingIcon from '../LoadingIcon';
+import ConnectionsActions from '../../actions/ConnectionsActions';
 
 const title = 'Network mapping';
 
@@ -36,86 +38,144 @@ class WizardNetworks extends Component {
 
   constructor(props) {
     super(props)
-    let networks = this.props.data.networks
-    networks.forEach(network => {network.selected = false})
-    /* this.props.data.vms.forEach((vm) => {
-      if (vm.selected) {
-        networks.forEach((network) => {
-          if (vm.networks.indexOf(network.id) != -1) {
-            network.selected = true
-            if (network.migrateNetwork == null) {
-              network.migrateNetwork = "Create new"
-            }
-          }
+
+    this.networkOptions = [] // [{ label: "Create new", value: null }]
+    if (this.props.data.targetNetworks && this.props.data.targetNetworks.length) {
+      this.props.data.targetNetworks.forEach((network) => {
+        this.networkOptions.push({
+          label: network.name,
+          value: network.name
         })
-      }
-    }) */
+      }, this)
+    }
+
+    props.data.selectedInstances.forEach((vm) => {
+      ConnectionsActions.loadInstanceDetail({ id: this.props.data.sourceCloud.credential.id }, vm)
+    })
+
+    let valid = true
+    if (props.data.networks) {
+      props.data.networks.forEach(item => {
+        if (item.migrateNetwork === null) {
+          valid = false
+        }
+      })
+    } else {
+      valid = false
+    }
 
     this.state = {
-      networks: networks,
+      networks: props.data.networks || null,
       nextStep: "WizardOptions",
-      valid: true
+      valid: valid
     }
-
-    this.networkOptions = ["Create new"]
-    // this.props.data.targetNetworks.forEach((network) => {
-    targetNetworkMock.forEach((network) => {
-      this.networkOptions.push(network)
-    }, this)
   }
 
   componentWillMount() {
-    this.props.setWizardState(this.state)
+    WizardActions.updateWizardState(this.state)
     this.context.onSetTitle(title);
   }
 
+  componentWillReceiveProps(props) {
+    this.processProps(props)
+  }
+
+  processProps(props) {
+    let networks = []
+
+    props.data.selectedInstances.forEach((vm) => {
+      if (vm.devices && vm.devices.nics) {
+        vm.devices.nics.forEach((item) => {
+          let exists = false
+          networks.forEach(network => {
+            if (network.network_name == item.network_name) {
+              exists = true
+            }
+          })
+          if (!exists) {
+            if (!item.migrateNetwork) {
+              item.migrateNetwork = null
+            }
+            networks.push(item)
+          }
+        })
+      }
+    })
+
+    if (networks.length == 0) {
+      networks = null
+    }
+    this.setState({ networks: networks })
+  }
+
   handleChangeNetwork(event, network) {
     let index = this.state.networks.indexOf(network)
+    let valid = true
     let networks = this.state.networks
     networks[index].migrateNetwork = event.value
-    this.setState({ networks: networks }, () => {
-      this.props.setWizardState(this.state)
+    networks.forEach(item => {
+      if (item.migrateNetwork === null) {
+        valid = false
+      }
+    })
+
+    this.setState({
+      networks: networks,
+      valid: valid
+    }, () => {
+      WizardActions.updateWizardState(this.state)
     })
   }
 
   render() {
-    let _this = this
-    let networks = this.state.networks.map((network, index) => {
-      if (network.selected || true) {
-        return (
-          <div className="item" key={"networks_" + index}>
-            <div className="cell cell-icon">
-              <div className="icon network"></div>
-              <span className="details">
-                {network.name}
-              </span>
-            </div>
-            <div className="cell">
-              <div className="arrow"></div>
+    if (this.state.networks != null) {
+      let networks = this.state.networks.map((network, index) => {
+        if (network.selected || true) {
+          return (
+            <div className="item" key={"networks_" + index}>
+              <div className="cell cell-icon">
+                <div className="icon network"></div>
+                <span className="details">
+                  {network.network_name}
+                </span>
+              </div>
+              <div className="cell">
+                <div className="arrow"></div>
+              </div>
+              <div className="cell">
+                <Dropdown
+                  options={this.networkOptions}
+                  onChange={(e) => this.handleChangeNetwork(e, network)}
+                  value={network.migrateNetwork}
+                />
+              </div>
             </div>
-            <div className="cell">
-              <Dropdown
-                options={_this.networkOptions}
-                onChange={(e) => _this.handleChangeNetwork(e, network)}
-                value={network.migrateNetwork}
-              />
+          )
+        } else {
+          return null
+        }
+      }, this)
+
+      return (
+        <div className={s.root}>
+          <div className={s.container}>
+            <div className="items-list">
+              {networks}
             </div>
           </div>
-        )
-      } else {
-        return null
-      }
-    })
-
-    return (
-      <div className={s.root}>
-        <div className={s.container}>
-          <div className="items-list">
-            {networks}
+        </div>
+      );
+    } else {
+      return (
+        <div className={s.root}>
+          <div className={s.container}>
+            <div className="items-list">
+              <LoadingIcon text="Loading networks..." />
+            </div>
           </div>
         </div>
-      </div>
-    );
+      );
+    }
   }
 
 }

+ 32 - 7
src/components/WizardOptions/WizardOptions.js

@@ -22,6 +22,7 @@ import s from './WizardOptions.scss';
 import Dropdown from '../NewDropdown';
 import WizardActions from '../../actions/WizardActions';
 import WizardStore from '../../stores/WizardStore';
+import NotificationActions from '../../actions/NotificationActions';
 
 
 const title = 'Migration Options';
@@ -46,13 +47,14 @@ class WizardOptions extends Reflux.Component {
       valid: true,
       nextStep: "WizardSchedule",
       formSubmitted: false,
-      showAdvancedOptions: false
+      nextCallback: (e) => this.nextCallback(e),
+      showAdvancedOptions: props.data.showAdvancedOptions
     }
   }
 
   componentWillMount() {
     super.componentWillMount.call(this)
-    this.props.setWizardState(this.state)
+    WizardActions.updateWizardState(this.state)
     this.context.onSetTitle(title)
   }
 
@@ -80,23 +82,46 @@ class WizardOptions extends Reflux.Component {
   }
 
   updateWizard() {
-    this.props.setWizardState(this.state)
+    WizardActions.updateWizardState(this.state)
   }
 
   isValid(field) {
-    if (field.required && this.state.formSubmitted) {
-      if (this.state.currentCloudData[field.name].length == 0) {
+    if (field.required && this.state.formSubmitted && field.name != "network_map") {
+      if (!this.state.destination_environment[field.name]) {
         return false
       } else {
-        return true
+        if (this.state.destination_environment[field.name].trim().length == 0) {
+          return false
+        } else {
+          return true
+        }
       }
     } else {
       return true
     }
   }
 
+  nextCallback(callback) {
+    let valid = true
+    this.setState({ formSubmitted: true }, () => {
+      this.state.targetCloud.cloudRef["import_" + this.state.migrationType].fields.forEach(field => {
+        if (!this.isValid(field)) {
+          valid = false
+        }
+      })
+      if (callback && valid) {
+        callback()
+      }
+      if (!valid) {
+        NotificationActions.notify("Please fill all required fields", "error")
+      }
+    })
+  }
+
   toggleAdvancedOptions() {
-    this.setState({ showAdvancedOptions: !this.state.showAdvancedOptions })
+    let showAdvancedOptions = !this.state.showAdvancedOptions
+    this.setState({ showAdvancedOptions: showAdvancedOptions })
+    WizardActions.updateWizardState({ showAdvancedOptions: showAdvancedOptions })
   }
 
   handleOptionsFieldChange(e, field) {

+ 8 - 0
src/components/WizardOptions/WizardOptions.scss

@@ -93,6 +93,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
       &.required, &.showAdvanced {
         display: block;
       }
+      &.error {
+        input {
+          border-color: $red;
+        }
+        :global(.Dropdown-control) {
+          border-color: $red;
+        }
+      }
     }
   }
   &:after {

+ 5 - 9
src/components/WizardSummary/WizardSummary.js

@@ -100,7 +100,7 @@ class WizardSummary extends Component {
         return (
           <div className="item" key={"Network_" + index}>
             <span className="cell">
-              <TextTruncate line={1} text={network.name} truncateText="..." />
+              <TextTruncate line={1} text={network.network_name} truncateText="..." />
             </span>
             <span className="cell">
               <div className="arrow"></div>
@@ -124,21 +124,21 @@ class WizardSummary extends Component {
           <div className={s.columnLeft}>
             <div className={s.group}>
               <h3>
-                Migration
-                <InfoIcon text="Helptext" />
+                Overview
+
               </h3>
               <div className={s.values}>
                 <div className={s.row}>
                   <span>Source: <br /> </span>
                   <span>
-                    <span className={s.cloudBox}>{this.props.summary.sourceCloud.name}</span> <br />
+                    <span className={s.cloudBox}>{this.props.summary.sourceCloud.name}</span>
                     {this.props.summary.sourceCloud.credential.name}
                   </span>
                 </div>
                 <div className={s.row}>
                   <span>Target:</span>
                   <span>
-                    <span className={s.cloudBox}>{this.props.summary.targetCloud.name}</span> <br />
+                    <span className={s.cloudBox}>{this.props.summary.targetCloud.name}</span>
                     {this.props.summary.targetCloud.credential.name}
                   </span>
                 </div>
@@ -147,7 +147,6 @@ class WizardSummary extends Component {
             <div className={s.group}>
               <h3>
                 Options
-                <InfoIcon text="Helptext" />
               </h3>
               <div className={s.values}>
                 <div className={s.row + " " + s.migrationType + " " + this.props.summary.migrationType}>
@@ -162,7 +161,6 @@ class WizardSummary extends Component {
             <div className={s.group}>
               <h3>
                 Schedule
-                <InfoIcon text="Helptext" />
               </h3>
               <div className={s.instances + " items-list"}>
                 {schedules}
@@ -173,7 +171,6 @@ class WizardSummary extends Component {
             <div className={s.group}>
               <h3>
                 Instances
-                <InfoIcon text="Helptext" />
               </h3>
               <div className={s.instances + " items-list"}>
                 {instances}
@@ -182,7 +179,6 @@ class WizardSummary extends Component {
             <div className={s.group}>
               <h3>
                 Networks
-                <InfoIcon text="Helptext" />
               </h3>
               <div className={s.networks + " items-list"}>
                 {networks}

+ 5 - 2
src/components/WizardSummary/WizardSummary.scss

@@ -94,11 +94,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   border: 1px solid $gray-dark;
   border-radius: 4px;
   color: $black;
-  line-height: 16px;
+  line-height: 18px;
   text-align: center;
   margin-right: 8px;
   text-transform: uppercase;
-  padding: 0 2px;
+  padding: 0 4px;
+  font-size: 9px;
+  font-weight: $weight-semibold;
+
 }
 .instances {
   :global(.item) {

+ 18 - 7
src/components/WizardTarget/WizardTarget.js

@@ -20,8 +20,7 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
 import s from './WizardTarget.scss';
 import CloudItem from '../CloudItem';
 import ConnectionsActions from '../../actions/ConnectionsActions';
-import ConnectionStore from '../../stores/ConnectionsStore';
-import { networkMock } from '../../config';
+import WizardActions from '../../actions/WizardActions';
 
 const title = 'Select your target cloud';
 
@@ -42,8 +41,6 @@ class WizardTarget extends Component {
   constructor(props) {
     super(props)
 
-    this.store = ConnectionStore
-
     this.state = {
       valid: false,
       clouds: this.props.clouds,
@@ -60,7 +57,7 @@ class WizardTarget extends Component {
         if (item.name == this.props.cloud.name) {
           item.selected = true
           item.credentialSelected = this.props.cloud.credentials
-          this.props.setWizardState({ valid: true, nextStep: "WizardVms" })
+          WizardActions.updateWizardState({ valid: true, nextStep: "WizardVms" })
         }
       }, this)
     }
@@ -92,17 +89,31 @@ class WizardTarget extends Component {
 
       this.setState({ clouds: newCloudsState }, () => {
         // we're all good, next step
-        this.props.setWizardState({
+        WizardActions.updateWizardState({
           targetCloud: Object.assign({}, cloudData),
           selected: selected,
           valid: true,
+          nextCallback: (e) => this.nextCallback(e),
           nextStep: "WizardVms",
-          networks: networkMock // TODO: Change mock here
+          networks: null
         })
       })
     }
   }
 
+  nextCallback(callback) {
+    let connection = this.props.cloud.credential
+
+    if (this.props.cloud.credential && this.props.cloud.credential.id === this.props.cloud.credential.name) {
+      connection = this.state.connections.filter(item => item.name === connection.name)[0]
+    }
+
+    ConnectionsActions.loadNetworks(connection)
+    if (callback) {
+      callback()
+    }
+  }
+
   addCredentialsCallback(data) {
     let targetCloud = null
 

+ 2 - 2
src/stores/ConnectionsStore/ConnectionsStore.js

@@ -136,13 +136,13 @@ class ConnectionsStore extends Reflux.Store
   }
 
   onUpdateProvider(provider) {
-    let allCLouds = this.state.allClouds
+    let allClouds = this.state.allClouds
     for (var i in allClouds) {
       if (allClouds[i].name == provider.name) {
         allClouds[i] = provider
       }
     }
-    this.setState({ allClouds: allCLouds })
+    this.setState({ allClouds: allClouds })
   }
 
   onAssignConnectionProvider() {

+ 10 - 3
src/stores/MigrationStore/MigrationStore.js

@@ -164,9 +164,9 @@ class MigrationStore extends Reflux.Store
         execIndex = i
       }
     })
-    console.log(replicas[index].executions)
+
     replicas[index].executions.splice(execIndex, 1)
-    console.log(replicas[index].executions)
+
     if (replicas[index].executions[replicas[index].executions]) {
       replicas[index].tasks = replicas[index].executions[replicas[index].executions.length - 1].tasks
       replicas[index].status = replicas[index].executions[replicas[index].executions.length - 1].status
@@ -174,7 +174,7 @@ class MigrationStore extends Reflux.Store
       replicas[index].tasks = []
       replicas[index].status = null
     }
-    console.log(replicas[index])
+
 
     this.setState({ replicas: replicas })
   }
@@ -221,6 +221,13 @@ class MigrationStore extends Reflux.Store
     this.setState({migrations: migrations})
   }
 
+  onDeleteReplicaCompleted(migration) {
+    let replicas = this.state.replicas
+    let index = replicas.indexOf(migration)
+    replicas.splice(index, 1)
+    this.setState({replicas: replicas})
+  }
+
   onExecuteReplicaCompleted(replica, response) {
     let replicas = this.state.replicas
     replicas.forEach((item, index) => {

+ 52 - 2
src/stores/WizardStore/WizardStore.js

@@ -42,9 +42,10 @@ class UsersStore extends Reflux.Store
     instances: null,
     selectedInstances: [],
     vms: null,
+    showAdvancedOptions: false,
     destination_environment: {},
-    networks: networkMock, // TODO: Change mock here
-    targetNetworks: targetNetworkMock, // TODO: Change mock here
+    networks: null,
+    targetNetworks: null,
     selected: false,
     wizardStarted: false
   }
@@ -108,6 +109,55 @@ class UsersStore extends Reflux.Store
     this.setState({ instances: [] })
   }
 
+  onLoadInstanceDetail(endpoint, instance) {
+    let projectId = Reflux.GlobalState.userStore.currentUser.project.id
+    let instanceNameBase64 = btoa(instance.name)
+    let url = `${servicesUrl.coriolis}/${projectId}/endpoints/${endpoint.id}/instances/${instanceNameBase64}`
+
+    Api.sendAjaxRequest({
+      url: url,
+      method: "GET"
+    }).then(response => {
+      ConnectionsActions.loadInstanceDetail.completed(response)
+    }, ConnectionsActions.loadInstanceDetail.failed)
+      .catch(ConnectionsActions.loadInstanceDetail.failed);
+  }
+
+  onLoadInstanceDetailCompleted(response) {
+    let selectedInstances = this.state.selectedInstances
+    selectedInstances.forEach((item, index) => {
+      if (item.id === response.data.instance.id) {
+        selectedInstances[index] = response.data.instance
+      }
+    })
+    console.log("selectedInstances", selectedInstances)
+    this.setState({ selectedInstances: selectedInstances })
+  }
+
+  onLoadNetworks(endpoint) {
+    this.setState({
+      targetNetworks: null
+    })
+    let projectId = Reflux.GlobalState.userStore.currentUser.project.id
+
+    let url = `${servicesUrl.coriolis}/${projectId}/endpoints/${endpoint.id}/networks`
+
+    Api.sendAjaxRequest({
+      url: url,
+      method: "GET"
+    }).then(ConnectionsActions.loadNetworks.completed, ConnectionsActions.loadNetworks.failed)
+      .catch(ConnectionsActions.loadNetworks.failed);
+  }
+
+  onLoadNetworksCompleted(response) {
+    console.log(response.data.networks)
+    if (response.data.networks) {
+      this.setState({
+        targetNetworks: response.data.networks
+      })
+    }
+  }
+
   onNewState() {
     this.setState(this.blankState)
   }