Coverage for mlos_bench/mlos_bench/services/remote/azure/azure_network_services.py: 94%

34 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-06 00:35 +0000

1# 

2# Copyright (c) Microsoft Corporation. 

3# Licensed under the MIT License. 

4# 

5""" 

6A collection Service functions for managing virtual networks on Azure. 

7""" 

8 

9import logging 

10 

11from typing import Any, Callable, Dict, List, Optional, Tuple, Union 

12 

13from mlos_bench.environments.status import Status 

14from mlos_bench.services.base_service import Service 

15from mlos_bench.services.remote.azure.azure_deployment_services import AzureDeploymentService 

16from mlos_bench.services.types.network_provisioner_type import SupportsNetworkProvisioning 

17from mlos_bench.util import merge_parameters 

18 

19_LOG = logging.getLogger(__name__) 

20 

21 

22class AzureNetworkService(AzureDeploymentService, SupportsNetworkProvisioning): 

23 """ 

24 Helper methods to manage Virtual Networks on Azure. 

25 """ 

26 

27 # Azure Compute REST API calls as described in 

28 # https://learn.microsoft.com/en-us/rest/api/virtualnetwork/virtual-networks?view=rest-virtualnetwork-2023-05-01 

29 

30 # From: https://learn.microsoft.com/en-us/rest/api/virtualnetwork/virtual-networks?view=rest-virtualnetwork-2023-05-01 

31 _URL_DEPROVISION = ( 

32 "https://management.azure.com" + 

33 "/subscriptions/{subscription}" + 

34 "/resourceGroups/{resource_group}" + 

35 "/providers/Microsoft.Network" + 

36 "/virtualNetwork/{vnet_name}" + 

37 "/delete" + 

38 "?api-version=2023-05-01" 

39 ) 

40 

41 def __init__(self, 

42 config: Optional[Dict[str, Any]] = None, 

43 global_config: Optional[Dict[str, Any]] = None, 

44 parent: Optional[Service] = None, 

45 methods: Union[Dict[str, Callable], List[Callable], None] = None): 

46 """ 

47 Create a new instance of Azure Network services proxy. 

48 

49 Parameters 

50 ---------- 

51 config : dict 

52 Free-format dictionary that contains the benchmark environment 

53 configuration. 

54 global_config : dict 

55 Free-format dictionary of global parameters. 

56 parent : Service 

57 Parent service that can provide mixin functions. 

58 methods : Union[Dict[str, Callable], List[Callable], None] 

59 New methods to register with the service. 

60 """ 

61 super().__init__( 

62 config, global_config, parent, 

63 self.merge_methods(methods, [ 

64 # SupportsNetworkProvisioning 

65 self.provision_network, 

66 self.deprovision_network, 

67 self.wait_network_deployment, 

68 ]) 

69 ) 

70 if not self._deploy_template: 

71 raise ValueError("AzureNetworkService requires a deployment template:\n" 

72 + f"config={config}\nglobal_config={global_config}") 

73 

74 def _set_default_params(self, params: dict) -> dict: # pylint: disable=no-self-use 

75 # Try and provide a semi sane default for the deploymentName if not provided 

76 # since this is a common way to set the deploymentName and can same some 

77 # config work for the caller. 

78 if "vnetName" in params and "deploymentName" not in params: 

79 params["deploymentName"] = f"{params['vnetName']}-deployment" 

80 _LOG.info("deploymentName missing from params. Defaulting to '%s'.", params["deploymentName"]) 

81 return params 

82 

83 def wait_network_deployment(self, params: dict, *, is_setup: bool) -> Tuple[Status, dict]: 

84 """ 

85 Waits for a pending operation on an Azure VM to resolve to SUCCEEDED or FAILED. 

86 Return TIMED_OUT when timing out. 

87 

88 Parameters 

89 ---------- 

90 params : dict 

91 Flat dictionary of (key, value) pairs of tunable parameters. 

92 is_setup : bool 

93 If True, wait for VM being deployed; otherwise, wait for successful deprovisioning. 

94 

95 Returns 

96 ------- 

97 result : (Status, dict) 

98 A pair of Status and result. 

99 Status is one of {PENDING, SUCCEEDED, FAILED, TIMED_OUT} 

100 Result is info on the operation runtime if SUCCEEDED, otherwise {}. 

101 """ 

102 return self._wait_deployment(params, is_setup=is_setup) 

103 

104 def provision_network(self, params: dict) -> Tuple[Status, dict]: 

105 """ 

106 Deploy a virtual network, if necessary. 

107 

108 Parameters 

109 ---------- 

110 params : dict 

111 Flat dictionary of (key, value) pairs of tunable parameters. 

112 NetworkEnv tunables are variable parameters that, together with the 

113 NetworkEnv configuration, are sufficient to provision a virtual network. 

114 

115 Returns 

116 ------- 

117 result : (Status, dict={}) 

118 A pair of Status and result. The result is the input `params` plus the 

119 parameters extracted from the response JSON, or {} if the status is FAILED. 

120 Status is one of {PENDING, SUCCEEDED, FAILED} 

121 """ 

122 return self._provision_resource(params) 

123 

124 def deprovision_network(self, params: dict, ignore_errors: bool = True) -> Tuple[Status, dict]: 

125 """ 

126 Deprovisions the virtual network on Azure by deleting it. 

127 

128 Parameters 

129 ---------- 

130 params : dict 

131 Flat dictionary of (key, value) pairs of tunable parameters. 

132 ignore_errors : boolean 

133 Whether to ignore errors (default) encountered during the operation 

134 (e.g., due to dependent resources still in use). 

135 

136 Returns 

137 ------- 

138 result : (Status, dict={}) 

139 A pair of Status and result. The result is always {}. 

140 Status is one of {PENDING, SUCCEEDED, FAILED} 

141 """ 

142 params = self._set_default_params(params) 

143 config = merge_parameters( 

144 dest=self.config.copy(), 

145 source=params, 

146 required_keys=[ 

147 "subscription", 

148 "resourceGroup", 

149 "deploymentName", 

150 "vnetName", 

151 ] 

152 ) 

153 _LOG.info("Deprovision Network: %s", config["vnetName"]) 

154 _LOG.info("Deprovision deployment: %s", config["deploymentName"]) 

155 (status, results) = self._azure_rest_api_post_helper(config, self._URL_DEPROVISION.format( 

156 subscription=config["subscription"], 

157 resource_group=config["resourceGroup"], 

158 vnet_name=config["vnetName"], 

159 )) 

160 if ignore_errors and status == Status.FAILED: 

161 _LOG.warning("Ignoring error: %s", results) 

162 status = Status.SUCCEEDED 

163 return (status, results)