/* Copyright (C) 2017 Cloudbase Solutions SRL This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ // @flow import moment from 'moment' import Api from '../utils/ApiCaller' import type { Assessment, VmItem, VmSize } from '../types/Assessment' const azureUrl = 'https://management.azure.com/' const defaultApiVersion = '2017-11-11-preview' const resourceGroupsUrl = (opts: { subscriptionId: string }) => `/subscriptions/${opts.subscriptionId}/resourceGroups` const projectsUrl = ({ resourceGroupName, ...other }) => `${resourceGroupsUrl({ ...other })}/${resourceGroupName}/providers/Microsoft.Migrate/projects` const groupsUrl = ({ projectName, ...other }) => `${projectsUrl({ ...other })}/${projectName}/groups` const assessmentsUrl = ({ groupName, ...other }) => `${groupsUrl({ ...other })}/${groupName}/assessments` const assessmentDetailsUrl = ({ assessmentName, ...other }) => `${assessmentsUrl({ ...other })}/${assessmentName}` const assessedVmsUrl = ({ ...other }) => `${assessmentDetailsUrl({ ...other })}/assessedMachines` class Util { static buildUrl(baseUrl: string, apiVersion?: string): string { const url = `/proxy/${azureUrl + baseUrl}?api-version=${apiVersion || defaultApiVersion}` return url } static sortAssessments(assessments) { assessments.sort((a, b) => { return moment(b.properties.updatedTimestamp).toDate().getTime() - moment(a.properties.updatedTimestamp) }) return assessments } static checkQueues(queues, requestIds, callback) { if (requestIds[0] !== requestIds[1]) { return } let doneQeues = queues.filter(q => q === 0).length if (doneQeues === queues.length) { callback() } } static isResponseValid(response): boolean { if (response && response.data && response.data.error) { const error = response.data.error console.error('%c', 'color: #D0021B', `${error.code}: ${error.message}`) return false } return true } static validateResponse(response, resolveData): Promise { if (!this.isResponseValid(response)) { return Promise.reject() } if (resolveData) { return Promise.resolve(resolveData) } return Promise.resolve(response) } } class AzureSource { static authenticate(username: string, password: string): Promise { return Api.send({ url: '/azure-login', method: 'POST', data: { username, password }, }).then(response => { let entries = Object.keys(response.data.tokenCache)[0] let accessToken = response.data.tokenCache[entries][0].accessToken Api.setDefaultHeader('Authorization', `Bearer ${accessToken}`) return response.data }) } static getResourceGroups(subscriptionId: string): Promise<$PropertyType[]> { return Api.get(Util.buildUrl(resourceGroupsUrl({ subscriptionId }), '2017-08-01')).then(response => { return Util.validateResponse(response, response.data.value) }) } static previousReqId: string static getAssessments(subscriptionId: string, resourceGroupName: string): Promise { let cancelId = subscriptionId + resourceGroupName if (this.previousReqId) { Api.cancelRequests(this.previousReqId) } this.previousReqId = cancelId // Load Projects return Api.send({ url: Util.buildUrl(projectsUrl({ resourceGroupName, subscriptionId })), cancelId, }).then(projectsResponse => { if (!Util.isResponseValid(projectsResponse)) { return [] } let projects = projectsResponse.data.value.filter(p => p.type === 'Microsoft.Migrate/projects') // Load groups for each project return Promise.all(projects.map(project => { return Api.send({ url: Util.buildUrl(groupsUrl({ projectName: project.name, subscriptionId, resourceGroupName })), cancelId, }).then(groupsResponse => { if (!Util.isResponseValid(groupsResponse)) { return null } return groupsResponse.data.value.map(group => { return { ...group, project } }) }) })) }).then(groupsResponses => { let groups = [] groupsResponses.filter(r => r !== null).forEach(validGroupsReponse => { groups = groups.concat(validGroupsReponse) }) // Load assessments for each group return Promise.all(groups.map(group => { // $FlowIgnore return Api.send({ url: Util.buildUrl(assessmentsUrl({ subscriptionId, resourceGroupName, projectName: group.project.name, groupName: group.name })), cancelId, }).then(assessmentResponse => { if (!Util.isResponseValid(assessmentResponse)) { return null } return assessmentResponse.data.value.map(assessment => { return { ...assessment, group, project: group.project } }) }) })) }).then(assessementsResponses => { let assessments = [] assessementsResponses.filter(r => r !== null).forEach(validAssessmentsResponse => { assessments = assessments.concat(validAssessmentsResponse) }) return Util.sortAssessments(assessments) }) } static getAssessmentDetails(info: Assessment): Promise { return Api.get(Util.buildUrl(assessmentDetailsUrl({ ...info, subscriptionId: info.connectionInfo.subscription_id }))).then(response => { return Util.validateResponse(response, { ...response.data, ...info }) }) } static getAssessedVms(info: Assessment): Promise { return Api.get(Util.buildUrl(assessedVmsUrl({ ...info, subscriptionId: info.connectionInfo.subscription_id }))).then(response => { if (!Util.isResponseValid(response)) { return [] } let vms = response.data.value vms.sort((a, b) => { let getLabel = item => `${item.properties.datacenterContainer}/${item.properties.displayName}` return getLabel(a).localeCompare(getLabel(b)) }) return vms }) } static getVmSizes(info: Assessment): Promise { return Api.get(Util.buildUrl(`/subscriptions/${info.connectionInfo.subscription_id}/providers/Microsoft.Compute/locations/${info.location}/vmSizes`, '2017-12-01')).then(response => { return Util.validateResponse(response, response.data.value) }) } } export default AzureSource