AzureSource.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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 moment from 'moment'
  15. import Api from '../utils/ApiCaller'
  16. import type { Assessment, VmItem, VmSize } from '../@types/Assessment'
  17. import DomUtils from '../utils/DomUtils'
  18. const azureUrl = 'https://management.azure.com/'
  19. const defaultApiVersion = '2019-10-01'
  20. const resourceGroupsUrl = (opts: { subscriptionId: string }) => `/subscriptions/${opts.subscriptionId}/resourceGroups`
  21. const projectsUrl = ({ resourceGroupName, ...other }: any) => `${resourceGroupsUrl({ ...other })}/${resourceGroupName}/providers/Microsoft.Migrate/assessmentprojects`
  22. const groupsUrl = ({ projectName, ...other }: any) => `${projectsUrl({ ...other })}/${projectName}/groups`
  23. const assessmentsUrl = ({ groupName, ...other }: any) => `${groupsUrl({ ...other })}/${groupName}/assessments`
  24. const assessmentDetailsUrl = ({ assessmentName, ...other }: any) => `${assessmentsUrl({ ...other })}/${assessmentName}`
  25. const assessedVmsUrl = ({ ...other }) => `${assessmentDetailsUrl({ ...other })}/assessedMachines`
  26. class Util {
  27. static buildUrl(baseUrl: string, apiVersion?: string): string {
  28. const url = `/proxy/${DomUtils.encodeToBase64Url(`${azureUrl + baseUrl}?api-version=${apiVersion || defaultApiVersion}`)}`
  29. return url
  30. }
  31. static sortAssessments(assessments: any[]) {
  32. assessments
  33. .sort((a: any, b: any) => moment(b.properties.updatedTimestamp)
  34. .toDate().getTime() - moment(a.properties.updatedTimestamp).toDate().getTime())
  35. return assessments
  36. }
  37. static checkQueues(queues: any[], requestIds: any, callback: any) {
  38. if (requestIds[0] !== requestIds[1]) {
  39. return
  40. }
  41. const doneQeues = queues.filter(q => q === 0).length
  42. if (doneQeues === queues.length) {
  43. callback()
  44. }
  45. }
  46. static isResponseValid(response: any): boolean {
  47. if (response && response.data && response.data.error) {
  48. const error = response.data.error
  49. console.error('%c', 'color: #D0021B', `${error.code}: ${error.message}`)
  50. return false
  51. }
  52. return true
  53. }
  54. static validateResponse(response: any, resolveData: any): Promise<any> {
  55. if (!this.isResponseValid(response)) {
  56. return Promise.reject()
  57. }
  58. if (resolveData) {
  59. return Promise.resolve(resolveData)
  60. }
  61. return Promise.resolve(response)
  62. }
  63. }
  64. class AzureSource {
  65. static authenticate(connectionInfo: any): Promise<any> {
  66. return Api.send({
  67. url: '/azure-login',
  68. method: 'POST',
  69. data: connectionInfo,
  70. }).then(response => {
  71. const entries = Object.keys(response.data.tokenCache)[0]
  72. const accessToken = response.data.tokenCache[entries][0].accessToken
  73. Api.setDefaultHeader('Authorization', `Bearer ${accessToken}`)
  74. return response.data
  75. })
  76. }
  77. static getResourceGroups(subscriptionId: string): Promise<Assessment['group'][]> {
  78. return Api.get(Util.buildUrl(resourceGroupsUrl({ subscriptionId }), '2017-08-01')).then(response => Util.validateResponse(response, response.data.value))
  79. }
  80. static previousReqId: string
  81. static async getAssessments(subscriptionId: string, resourceGroupName: string, skipLog?: boolean | null): Promise<Assessment[]> {
  82. const cancelId = subscriptionId + resourceGroupName
  83. if (this.previousReqId) {
  84. Api.cancelRequests(this.previousReqId)
  85. }
  86. this.previousReqId = cancelId
  87. // Load Projects
  88. let projects: any[] = []
  89. const projectsResponse = await Api.send({
  90. url: Util.buildUrl(projectsUrl({ resourceGroupName, subscriptionId })),
  91. cancelId,
  92. skipLog,
  93. })
  94. if (!Util.isResponseValid(projectsResponse)) {
  95. projects = []
  96. }
  97. projects = projectsResponse.data.value.filter((p: any) => p.type === 'Microsoft.Migrate/assessmentprojects')
  98. // Load groups for each project
  99. const groupsResponses = await Promise.all(projects.map(async (project: any) => {
  100. let groups: any[] | null = null
  101. const groupsResponse = await Api.send({
  102. url: Util.buildUrl(
  103. groupsUrl({ projectName: project.name, subscriptionId, resourceGroupName }),
  104. ),
  105. cancelId,
  106. })
  107. if (!Util.isResponseValid(groupsResponse)) {
  108. groups = null
  109. }
  110. groups = groupsResponse.data.value.map((group: any) => ({ ...group, project }))
  111. return groups
  112. }))
  113. let groups: any[] = []
  114. groupsResponses.filter(r => r !== null).forEach(validGroupsReponse => {
  115. groups = groups.concat(validGroupsReponse)
  116. })
  117. // Load assessments for each group
  118. return Promise.all(groups.map(group => Api.send({
  119. url: Util.buildUrl(assessmentsUrl({
  120. subscriptionId, resourceGroupName, projectName: group.project.name, groupName: group.name,
  121. })),
  122. cancelId,
  123. }).then(assessmentResponse => {
  124. if (!Util.isResponseValid(assessmentResponse)) {
  125. return null
  126. }
  127. return assessmentResponse.data.value.map((assessment: Assessment) => ({
  128. ...assessment,
  129. group,
  130. project: group.project,
  131. properies: {
  132. ...assessment.properties,
  133. azureLocation: assessment.properties.azureLocation.toLowerCase(),
  134. },
  135. }))
  136. })))
  137. }
  138. static async getAssessmentDetails(info: Assessment): Promise<Assessment> {
  139. const response = await Api.get(Util
  140. .buildUrl(
  141. assessmentDetailsUrl({ ...info, subscriptionId: info.connectionInfo.subscription_id }),
  142. ))
  143. const assessment: Assessment = await Util
  144. .validateResponse(response, { ...response.data, ...info })
  145. assessment.properties.azureLocation = assessment.properties.azureLocation.toLowerCase()
  146. return assessment
  147. }
  148. static getAssessedVms(info: Assessment): Promise<VmItem[]> {
  149. return Api.get(Util
  150. .buildUrl(assessedVmsUrl({ ...info, subscriptionId: info.connectionInfo.subscription_id })))
  151. .then(response => {
  152. if (!Util.isResponseValid(response)) {
  153. return []
  154. }
  155. const vms = response.data.value
  156. vms.sort((a: any, b: any) => {
  157. const getLabel = (item: any) => item.properties.displayName
  158. return getLabel(a).localeCompare(getLabel(b))
  159. })
  160. return vms
  161. })
  162. }
  163. static getVmSizes(info: Assessment): Promise<VmSize[]> {
  164. return Api.get(Util.buildUrl(`/subscriptions/${info.connectionInfo.subscription_id}/providers/Microsoft.Compute/locations/${info.location}/vmSizes`, '2017-12-01')).then(response => Util.validateResponse(response, response.data.value))
  165. }
  166. }
  167. export default AzureSource