PyPowerStore 1.9.0.0__tar.gz → 2.0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PKG-INFO +3 -2
  2. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/__init__.py +1 -1
  3. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/client.py +4 -1
  4. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/configuration.py +271 -19
  5. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/protection.py +55 -0
  6. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/provisioning.py +64 -13
  7. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/utils/constants.py +119 -13
  8. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/utils/helpers.py +14 -2
  9. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore.egg-info/PKG-INFO +3 -2
  10. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore.egg-info/SOURCES.txt +0 -1
  11. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/README.md +1 -1
  12. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/setup.py +3 -1
  13. PyPowerStore-1.9.0.0/LICENSE +0 -201
  14. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/powerstore_conn.py +0 -0
  15. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/utils/__init__.py +0 -0
  16. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore/utils/exception.py +0 -0
  17. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore.egg-info/dependency_links.txt +0 -0
  18. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore.egg-info/requires.txt +0 -0
  19. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/PyPowerStore.egg-info/top_level.txt +0 -0
  20. {PyPowerStore-1.9.0.0 → PyPowerStore-2.0.0.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 1.0
1
+ Metadata-Version: 1.1
2
2
  Name: PyPowerStore
3
- Version: 1.9.0.0
3
+ Version: 2.0.0.0
4
4
  Summary: Python Library for Dell PowerStore
5
5
  Home-page: https://github.com/dell/python-powerstore
6
6
  Author: Ansible Team at Dell
@@ -8,3 +8,4 @@ Author-email: ansible.team@dell.com
8
8
  License: UNKNOWN
9
9
  Description: UNKNOWN
10
10
  Platform: UNKNOWN
11
+ Classifier: License :: OSI Approved :: Apache Software License
@@ -2,6 +2,6 @@
2
2
  """__init__.py."""
3
3
 
4
4
  __title__ = 'PyPowerStore'
5
- __version__ = '1.9.0.0'
5
+ __version__ = '2.0.0.0'
6
6
  __author__ = 'Dell Technologies or its subsidiaries'
7
7
  __copyright__ = 'Copyright 2019 Dell Technologies'
@@ -247,7 +247,10 @@ class Client():
247
247
  if self.is_valid_response(response):
248
248
  response_json = None
249
249
  if response.status_code != 204:
250
- response_json = response.json()
250
+ if response.status_code == 201 and response.content == b'':
251
+ pass
252
+ else:
253
+ response_json = response.json()
251
254
  # check 'all_pages' required, response received is
252
255
  # partial(code 206) and contains info about total size of
253
256
  # the collection
@@ -901,7 +901,6 @@ class Configuration:
901
901
  # IP ports operations end
902
902
 
903
903
  # vCenter operations start
904
-
905
904
  def get_vcenters(self, filter_dict=None, all_pages=False):
906
905
  """Get all vcenters.
907
906
  :param filter_dict: (optional) Filter details
@@ -916,11 +915,18 @@ class Configuration:
916
915
  querystring = helpers.prepare_querystring(
917
916
  constants.SELECT_ID, filter_dict)
918
917
  LOG.info("Querystring: '%s'" % querystring)
919
- return self.config_client.request(
920
- constants.GET,
921
- constants.GET_VCENTER_LIST_URL.format(self.server_ip),
922
- querystring=querystring, all_pages=all_pages
923
- )
918
+ vcenter_list = self.config_client.\
919
+ request(constants.GET,
920
+ constants.GET_VCENTER_LIST_URL.format(self.server_ip),
921
+ querystring=querystring, all_pages=all_pages)
922
+
923
+ if vcenter_list:
924
+ resp_list = []
925
+ for vcenter in vcenter_list:
926
+ resp_dict = self.get_vcenter_details(vcenter['id'])
927
+ resp_list.append(resp_dict)
928
+ return resp_list
929
+ return vcenter_list
924
930
 
925
931
  def get_vcenter_details(self, vcenter_id):
926
932
  """Get vcenter details.
@@ -931,12 +937,11 @@ class Configuration:
931
937
  """
932
938
  LOG.info("Getting vcenter details by ID: '%s'" % vcenter_id)
933
939
  querystring = constants.VCENTER_DETAILS_QUERY
934
- if helpers.is_foot_hill_or_higher():
935
- querystring = {
936
- 'select': 'id,instance_uuid,address,username,'
937
- 'vendor_provider_status,'
938
- 'vendor_provider_status_l10n'
939
- }
940
+ if helpers.is_foot_hill_prime_or_higher():
941
+ querystring = constants.FHP_VCENTER_QUERY
942
+ elif helpers.is_foot_hill_or_higher():
943
+ querystring = constants.FHC_MALKA_VCENTER_QUERY
944
+
940
945
  return self.config_client.request(
941
946
  constants.GET,
942
947
  constants.GET_VCENTER_DETAILS_URL.format(self.server_ip,
@@ -945,20 +950,66 @@ class Configuration:
945
950
  )
946
951
 
947
952
  def modify_vcenter(self, vcenter_id, modify_param_dict):
948
- """Register VASA provider.
953
+ """Modify vcenter attributes.
949
954
  :param vcenter_id: ID of the vcenter
950
955
  :type vcenter_id: str
951
- :param modify_param_dict: Dict containing VASA provider credentials
956
+ :param modify_param_dict: Dict containing parameters for modification
952
957
  :type modify_param_dict: dict
953
958
  :return: Details of vcenter
954
959
  :rtype: dict
955
960
  """
956
- LOG.info("Registering VASA provider: '%s'" % vcenter_id)
961
+ LOG.info("Modifying vCenter attributes: '%s'" % vcenter_id)
957
962
  self.config_client.request(constants.PATCH,
958
963
  constants.MODIFY_VCENTER_URL.format(
959
964
  self.server_ip, vcenter_id),
960
965
  payload=modify_param_dict)
961
966
  return self.get_vcenter_details(vcenter_id)
967
+
968
+ def add_vcenter(self, add_params):
969
+ """
970
+ Add a vcenter to the unified PowerStore model.
971
+ vcenter can not be added to unified+ deployment
972
+ :param add_params: the parameters to add vcenter
973
+ :type add_params:dict
974
+ :return: ID of the vcenter if addition is successful
975
+ :rtype: dict
976
+ """
977
+ LOG.info("Adding a vcenter.")
978
+
979
+ payload = dict()
980
+ if add_params:
981
+ for key, values in add_params.items():
982
+ payload[key] = values
983
+
984
+ return self.config_client.\
985
+ request(constants.POST,
986
+ constants.ADD_VCENTER_URL.format(self.server_ip),
987
+ payload=payload)
988
+
989
+ def remove_vcenter(self, vcenter_id, delete_vasa_provider=None):
990
+ """
991
+ Remove vcenter from Unified PowerStore model.
992
+ vcenter can not be removed from unified+ deployment
993
+ :param vcenter_id: ID of the vcenter
994
+ :type vcenter_id: str
995
+ :param delete_vasa_provider: whether to remove a VASA provider.
996
+ Removal will only happen if the provider
997
+ is not connected to any other PowerStore
998
+ system
999
+ :type delete_vasa_provider: bool
1000
+ :return: None if success
1001
+ :rtype: None
1002
+ """
1003
+ LOG.info("Removing vcenter: {0}.".format(vcenter_id))
1004
+ payload = dict()
1005
+ if delete_vasa_provider is not None:
1006
+ payload['delete_vendor_provider'] = delete_vasa_provider
1007
+
1008
+ return self.config_client.\
1009
+ request(constants.DELETE,
1010
+ constants.REMOVE_VCENTER_URL.format(self.server_ip,
1011
+ vcenter_id),
1012
+ payload=payload)
962
1013
  # vCenter operations end
963
1014
 
964
1015
  # Appliance operations start
@@ -1761,7 +1812,6 @@ class Configuration:
1761
1812
  """
1762
1813
  LOG.info("Modifying remote support contact : '%s' with params '%s'" % (
1763
1814
  remote_support_contact_id, modify_parameters))
1764
- print(remote_support_contact_id)
1765
1815
  if helpers.is_foot_hill_or_higher():
1766
1816
  remote_support_contact_url = constants.MODIFY_REMOTE_SUPPORT_CONTACT_URL
1767
1817
  return self.config_client.request(
@@ -1843,10 +1893,10 @@ class Configuration:
1843
1893
  resp = self.config_client.request(
1844
1894
  constants.GET,
1845
1895
  constants.GET_LDAP_DOMAIN_LIST_URL.format(
1846
- self.server_ip, ldap_domain_name),
1896
+ self.server_ip),
1847
1897
  querystring=helpers.prepare_querystring(
1848
1898
  constants.LDAP_DOMAIN_DETAILS_QUERY,
1849
- name=constants.EQUALS + ldap_domain_name)
1899
+ domain_name=constants.EQUALS + ldap_domain_name)
1850
1900
  )
1851
1901
 
1852
1902
  filterable_keys = ['domain_name', 'id', 'protocol', 'ldap_server_type']
@@ -2044,7 +2094,209 @@ class Configuration:
2044
2094
  self.server_ip, ldap_account_id))
2045
2095
 
2046
2096
 
2047
- # LDAP Account operations end
2097
+ # LDAP Account operations end
2098
+
2099
+ # Virtual volume operations begin
2100
+
2101
+ def get_virtual_volume_list(self, filter_dict=None, all_pages=None):
2102
+ """Get all virtual volumes available on array.
2103
+ :param filter_dict: (optional) Filter details
2104
+ :type filter_dict: dict
2105
+ :param all_pages: (optional) Indicates whether to return all
2106
+ virtual volumes or not
2107
+ :type all_pages: bool
2108
+ :return: List of virtual volumes on array
2109
+ :rtype: list[dict]
2110
+ """
2111
+ LOG.info("Getting volumes with filter: '%s' and all_pages: %s"
2112
+ % (filter_dict, all_pages))
2113
+ querystring = helpers.prepare_querystring(
2114
+ constants.VIRTUAL_VOLUME_DETAILS_QUERY,
2115
+ filter_dict)
2116
+ if helpers.is_foot_hill_prime_or_higher():
2117
+ querystring = helpers.prepare_querystring(
2118
+ constants.VIRTUAL_VOLUME_FHP_DETAILS_QUERY,
2119
+ filter_dict)
2120
+ LOG.info("Querystring: '%s'" % querystring)
2121
+ return self.config_client.request(constants.GET,
2122
+ constants.GET_VIRTUAL_VOLUME_LIST_URL.format
2123
+ (self.server_ip), payload=None,
2124
+ querystring=querystring,
2125
+ all_pages=all_pages)
2126
+
2127
+ # Virtual volume operations end
2128
+
2129
+ # Storage container operations begin
2130
+
2131
+ def get_storage_container_list(self, filter_dict=None, all_pages=None):
2132
+ """Get all storage container available on array.
2133
+ :param filter_dict: (optional) Filter details
2134
+ :type filter_dict: dict
2135
+ :param all_pages: (optional) Indicates whether to return all
2136
+ Storage containers or not
2137
+ :type all_pages: bool
2138
+ :return: List of storage containers on array
2139
+ :rtype: list[dict]
2140
+ """
2141
+ LOG.info("Getting storage containers with filter: '%s' and all_pages: %s"
2142
+ % (filter_dict, all_pages))
2143
+ querystring = helpers.prepare_querystring(
2144
+ constants.STORAGE_CONTAINER_DETAILS_QUERY,
2145
+ filter_dict)
2146
+ LOG.info("Querystring: '%s'" % querystring)
2147
+ return self.config_client.request(constants.GET,
2148
+ constants.GET_STORAGE_CONTAINER_LIST_URL.format
2149
+ (self.server_ip), payload=None,
2150
+ querystring=querystring,
2151
+ all_pages=all_pages)
2152
+
2153
+ def get_storage_container_details(self, storage_container_id):
2154
+ """ Get details of a storage container instance.
2155
+
2156
+ :param storage_container_id: Unique identifier of the storage_container
2157
+ :type storage_container_id: str
2158
+ :return: storage container details
2159
+ :rtype: dict
2160
+ """
2161
+ LOG.info("Getting storage container details by ID: '%s'" % storage_container_id)
2162
+
2163
+ return self.config_client.request(
2164
+ constants.GET,
2165
+ constants.GET_STORAGE_CONTAINER_DETAILS_URL.format(
2166
+ self.server_ip, storage_container_id),
2167
+ querystring=constants.STORAGE_CONTAINER_DETAILS_QUERY)
2168
+
2169
+ def get_storage_container_details_by_name(self, storage_container_name):
2170
+ """ Get details of a storage container instance.
2171
+
2172
+ :param storage_container_name: storage container name
2173
+ :type storage_container_name: str
2174
+ :return: storage container details
2175
+ :rtype: dict
2176
+ """
2177
+ LOG.info("Getting storage container details by name: '%s'" % storage_container_name)
2178
+ resp = self.get_storage_container_list()
2179
+
2180
+ for container in resp:
2181
+ if container['name'] == storage_container_name:
2182
+ return self.get_storage_container_details(container['id'])
2183
+
2184
+ def create_storage_container(self, create_parameters):
2185
+ """ Create a storage_container.
2186
+
2187
+ :param create_parameters: Parameters for creating a storage_container
2188
+ :type create_parameters: dict
2189
+ :return: Unique identifier of the new storage container instance created
2190
+ :rtype: dict
2191
+ """
2192
+ LOG.info("creating storage container")
2193
+
2194
+ return self.config_client.request(
2195
+ constants.POST,
2196
+ constants.CREATE_STORAGE_CONTAINER_URL.format(
2197
+ self.server_ip), payload=create_parameters)
2198
+
2199
+ def modify_storage_container_details(self, storage_container_id, modify_parameters):
2200
+ """ Modifying storage container configuration.
2201
+
2202
+ :param storage_container_id: Unique ID of the storage container instance
2203
+ :type storage_container_id: str
2204
+ :param modify_parameters: Parameters for modifying storage container
2205
+ :type modify_parameters: dict
2206
+ :return: None
2207
+ :rtype: None
2208
+ """
2209
+ LOG.info("Modifying storage containert id: '%s'" % storage_container_id)
2210
+
2211
+ return self.config_client.request(
2212
+ constants.PATCH,
2213
+ constants.MODIFY_STORAGE_CONTAINER_URL.format(
2214
+ self.server_ip, storage_container_id), payload=modify_parameters)
2215
+
2216
+ def delete_storage_container(self, storage_container_id, delete_parameters):
2217
+ """ Delete a storage container.
2218
+
2219
+ :param storage_container_id: Unique ID of the storage container instance
2220
+ :type storage_container_id: str
2221
+ :return: None
2222
+ :rtype: None
2223
+ """
2224
+ LOG.info("Deleting storage container with id: '%s'" % storage_container_id)
2225
+
2226
+ return self.config_client.request(
2227
+ constants.DELETE,
2228
+ constants.DELETE_STORAGE_CONTAINER_URL.format(
2229
+ self.server_ip, storage_container_id), payload=delete_parameters)
2230
+ # Storage container operations end
2231
+
2232
+ # Storage container destination operations start
2233
+ def get_storage_container_destination_list(self, filter_dict=None, all_pages=None):
2234
+ """Get all storage container destination.
2235
+ :param filter_dict: (optional) Filter details
2236
+ :type filter_dict: dict
2237
+ :param all_pages: (optional) Indicates whether to return all
2238
+ Storage containers destination or not
2239
+ :type all_pages: bool
2240
+ :return: List of storage containers destination
2241
+ :rtype: list[dict]
2242
+ """
2243
+ LOG.info("Getting storage containers destination with filter: '%s' "
2244
+ "and all_pages: %s" % (filter_dict, all_pages))
2245
+ querystring = helpers.prepare_querystring(
2246
+ constants.STORAGE_CONTAINER_DETAILS_DESTINATION_QUERY, filter_dict)
2247
+ LOG.info("Querystring: '%s'" % querystring)
2248
+ return self.config_client.request(
2249
+ constants.GET,
2250
+ constants.GET_STORAGE_CONTAINER_DESTINATION_LIST_URL.format(
2251
+ self.server_ip),
2252
+ payload=None, querystring=querystring, all_pages=all_pages)
2253
+
2254
+ def get_storage_container_destination_details(self, storage_container_destination_id):
2255
+ """ Get details of a storage container destination instance.
2256
+
2257
+ :param storage_container_destination_id: Unique identifier of the
2258
+ storage container destination
2259
+ :type storage_container_destination_id: str
2260
+ :return: storage container destination details
2261
+ :rtype: dict
2262
+ """
2263
+ LOG.info("Getting storage container destination details by "
2264
+ "ID: '%s'" % storage_container_destination_id)
2265
+
2266
+ return self.config_client.request(
2267
+ constants.GET,
2268
+ constants.GET_STORAGE_CONTAINER_DESTINATION_DETAILS_URL.format(
2269
+ self.server_ip, storage_container_destination_id),
2270
+ querystring=constants.STORAGE_CONTAINER_DETAILS_DESTINATION_QUERY)
2271
+
2272
+ def create_storage_container_destination(self, create_destination_params):
2273
+ """Create a Storage Container Destination
2274
+ :param create_destination_params: parameter to create storage container
2275
+ destination
2276
+ :type create_destination_params: dict
2277
+ :return: Unique identifier of newly created storage container destination
2278
+ :rtype: dict
2279
+ """
2280
+ LOG.info("Creating storage container destination.")
2281
+ return self.config_client.request(
2282
+ constants.POST,
2283
+ constants.CREATE_STORAGE_CONTAINER_DESTINATION_URL.format(
2284
+ self.server_ip), payload=create_destination_params)
2285
+
2286
+ def delete_storage_container_destination(self, storage_container_destination_id):
2287
+ """Delete a storage container destination
2288
+ :param storage_container_destination_id: ID of storage container destination
2289
+ :type storage_container_destination_id: str
2290
+ :rtype: None
2291
+ """
2292
+ LOG.info("Deleting storage container destination with "
2293
+ "id: '%s'" % storage_container_destination_id)
2294
+ return self.config_client.request(
2295
+ constants.DELETE,
2296
+ constants.DELETE_STORAGE_CONTAINER_DESTINATION_URL.format(
2297
+ self.server_ip, storage_container_destination_id))
2298
+
2299
+ # Storage container destination operations end
2048
2300
 
2049
2301
  @staticmethod
2050
2302
  def _prepare_local_user_payload(**kwargs):
@@ -1142,6 +1142,61 @@ class ProtectionFunctions:
1142
1142
 
1143
1143
  # Replication Session end
1144
1144
 
1145
+ # Replication group start
1146
+ def get_replication_groups(self, filter_dict=None, all_pages=False):
1147
+ """Get all the replication groups
1148
+ :param filter_dict: (optional) Filter details
1149
+ :type filter_dict: dict
1150
+ :param all_pages: (optional) Indicates whether to return all elements
1151
+ or not
1152
+ :type all_pages: bool
1153
+ :return: replication groups.
1154
+ :rtype: list[dict]
1155
+ """
1156
+ LOG.info("Getting replication groups with filter: '%s' and "
1157
+ "all_pages: %s" % (filter_dict, all_pages))
1158
+ querystring = helpers.prepare_querystring(
1159
+ constants.REPLICATION_GROUP_QUERY, filter_dict)
1160
+ LOG.info("Querystring: '%s'" % querystring)
1161
+ return self.rest_client.request(
1162
+ constants.GET, constants.REPLICATION_GROUP_DETAILS_LIST_URL.format(
1163
+ self.server_ip), payload=None, querystring=querystring,
1164
+ all_pages=all_pages)
1165
+
1166
+ def get_replication_group_details(self, replication_group_id):
1167
+ """Getting the details of a replication group using ID
1168
+ :param replication_group_id: ID of the replication group
1169
+ :type replication_group_id: str
1170
+ :return: replication session details
1171
+ :rtype: dict
1172
+ """
1173
+ LOG.info("Getting replication session details by ID:"
1174
+ " '%s'" % replication_group_id)
1175
+
1176
+ return self.rest_client.request(
1177
+ constants.GET,
1178
+ constants.REPLICATION_GROUP_DETAILS_URL.format(
1179
+ self.server_ip, replication_group_id),
1180
+ querystring=constants.REPLICATION_GROUP_QUERY)
1181
+
1182
+ def get_replication_group_details_by_name(self, replication_group_name):
1183
+ """Get details of the replication group details using name
1184
+ :param replication_group_name: Name of the replication group
1185
+ :type replication_group_name: str
1186
+ :return: replication session details
1187
+ :rtype: list of dict
1188
+ """
1189
+ LOG.info("Getting replication group details by name:"
1190
+ " '%s'" % replication_group_name)
1191
+ return self.rest_client.request(
1192
+ constants.GET,
1193
+ constants.REPLICATION_GROUP_DETAILS_LIST_URL.format(
1194
+ self.server_ip),
1195
+ querystring=helpers.prepare_querystring(
1196
+ constants.REPLICATION_GROUP_QUERY,
1197
+ name=constants.EQUALS + replication_group_name))
1198
+ # Replication group end
1199
+
1145
1200
  # Remote System start
1146
1201
  def get_remote_systems(self, filter_dict=None, all_pages=False):
1147
1202
  """Get all remote systems.
@@ -64,7 +64,8 @@ class Provisioning:
64
64
 
65
65
  def create_volume(self, name, size, description=None,
66
66
  volume_group_id=None, protection_policy_id=None,
67
- performance_policy_id=None):
67
+ performance_policy_id=None, app_type=None,
68
+ app_type_other=None, appliance_id=None):
68
69
  """Create a volume.
69
70
 
70
71
  :param name: The name of the volume
@@ -73,13 +74,24 @@ class Provisioning:
73
74
  :param volume_group_id: (optional) The volume group ID
74
75
  :param protection_policy_id: (optional) The protection policy ID
75
76
  :param performance_policy_id: (optional) The performance policy ID
77
+ :param app_type: (optional) The application type
78
+ :param app_type_other: (optional) Describes application type when
79
+ app_type is set to other
80
+ :param appliance_id: (optional) The appliance ID
76
81
  """
82
+ if app_type is not None and not helpers.is_malka_or_higher():
83
+ raise Exception("'app_type' parameter is supported only from "
84
+ "Powerstore version 2.1.0.0 onwards")
85
+
77
86
  LOG.info("Creating volume: '%s'" % name)
78
87
  payload = self._prepare_create_volume_payload(name, size,
79
88
  description,
80
89
  volume_group_id,
81
90
  protection_policy_id,
82
- performance_policy_id)
91
+ performance_policy_id,
92
+ app_type,
93
+ app_type_other,
94
+ appliance_id)
83
95
  self.client.request(constants.POST,
84
96
  constants.VOLUME_CREATE_URL.format(
85
97
  self.server_ip), payload)
@@ -88,7 +100,10 @@ class Provisioning:
88
100
  description,
89
101
  volume_group_id,
90
102
  protection_policy_id,
91
- performance_policy_id):
103
+ performance_policy_id,
104
+ app_type,
105
+ app_type_other,
106
+ appliance_id):
92
107
 
93
108
  create_volume_dict = dict()
94
109
  if name is not None:
@@ -104,6 +119,12 @@ class Provisioning:
104
119
  if performance_policy_id is not None:
105
120
  create_volume_dict['performance_policy_id'] = \
106
121
  performance_policy_id
122
+ if app_type is not None:
123
+ create_volume_dict['app_type'] = app_type
124
+ if app_type_other is not None:
125
+ create_volume_dict['app_type_other'] = app_type_other
126
+ if appliance_id is not None:
127
+ create_volume_dict['appliance_id'] = appliance_id
107
128
 
108
129
  return create_volume_dict
109
130
 
@@ -122,8 +143,8 @@ class Provisioning:
122
143
  payload=None)
123
144
 
124
145
  def modify_volume(self, volume_id, name=None, description=None, size=None,
125
- protection_policy_id=None,
126
- performance_policy_id=None):
146
+ protection_policy_id=None, performance_policy_id=None,
147
+ app_type=None, app_type_other=None):
127
148
  """Modify a volume.
128
149
 
129
150
  :param volume_id: The volume ID
@@ -138,16 +159,26 @@ class Provisioning:
138
159
  :type protection_policy_id: str
139
160
  :param performance_policy_id: The performance policy ID
140
161
  :type performance_policy_id: str
162
+ :param app_type: The application type
163
+ :type app_type: str
164
+ :param app_type_other: Describes application type when
165
+ app_type is set to other
141
166
  :return: None if success else raise exception
142
167
  :rtype: None
143
168
  """
169
+ if app_type is not None and not helpers.is_malka_or_higher():
170
+ raise Exception("'app_type' parameter is supported only from "
171
+ "Powerstore version 2.1.0.0 onwards")
172
+
144
173
  LOG.info("Modifying volume: '%s'" % volume_id)
145
174
  payload = self.\
146
175
  _prepare_modify_volume_payload(name,
147
176
  description,
148
177
  size,
149
178
  protection_policy_id,
150
- performance_policy_id)
179
+ performance_policy_id,
180
+ app_type,
181
+ app_type_other)
151
182
  return self.client.request(
152
183
  constants.PATCH, constants.MODIFY_VOLUME_URL.format(
153
184
  self.server_ip, volume_id),
@@ -156,7 +187,9 @@ class Provisioning:
156
187
  def _prepare_modify_volume_payload(self, name=None, description=None,
157
188
  size=None,
158
189
  protection_policy_id=None,
159
- performance_policy_id=None):
190
+ performance_policy_id=None,
191
+ app_type=None,
192
+ app_type_other=None):
160
193
 
161
194
  modify_volume_dict = dict()
162
195
  if name is not None:
@@ -170,6 +203,10 @@ class Provisioning:
170
203
  if performance_policy_id is not None:
171
204
  modify_volume_dict['performance_policy_id'] = \
172
205
  performance_policy_id
206
+ if app_type is not None:
207
+ modify_volume_dict['app_type'] = app_type
208
+ if app_type_other is not None:
209
+ modify_volume_dict['app_type_other'] = app_type_other
173
210
 
174
211
  return modify_volume_dict
175
212
 
@@ -238,7 +275,7 @@ class Provisioning:
238
275
  performance_policy_id
239
276
 
240
277
  return clone_volume_dict
241
-
278
+
242
279
  def refresh_volume(self, volume_id, volume_id_to_refresh_from=None,
243
280
  create_backup_snap=None,
244
281
  backup_snap_name=None,
@@ -303,7 +340,7 @@ class Provisioning:
303
340
  backup_snap_performance_policy_id
304
341
 
305
342
  return refresh_volume_dict
306
-
343
+
307
344
  def restore_volume(self, volume_id, snap_id_to_restore_from=None,
308
345
  create_backup_snap=None,
309
346
  backup_snap_name=None,
@@ -1163,7 +1200,7 @@ class Provisioning:
1163
1200
  :type name: str
1164
1201
  :param description: (optional) Description for the clone volume group.
1165
1202
  :type description: str
1166
- :param protection_policy_id: (optional) Unique identifier of the protection
1203
+ :param protection_policy_id: (optional) Unique identifier of the protection
1167
1204
  policy to assign to the clone volume group
1168
1205
  :type protection_policy_id: str
1169
1206
  :return: Unique identifier of the new instance created if success else raise exception
@@ -1493,7 +1530,7 @@ class Provisioning:
1493
1530
  )
1494
1531
 
1495
1532
  # NAS Server methods
1496
-
1533
+
1497
1534
  def get_nas_servers(self, filter_dict=None, all_pages=False):
1498
1535
  """Get a list of nas servers.
1499
1536
 
@@ -1620,12 +1657,15 @@ class Provisioning:
1620
1657
  :rtype: dict
1621
1658
  """
1622
1659
  LOG.info("Getting filesystem details by ID: '%s'" % filesystem_id)
1660
+ querystring=constants.SELECT_ALL_FILESYSTEM
1661
+ if helpers.is_foot_hill_prime_or_higher():
1662
+ querystring=constants.SELECT_ALL_FILESYSTEM_PRIME
1623
1663
  return self.client.request(
1624
1664
  constants.GET,
1625
1665
  constants.GET_FILESYSTEM_DETAILS_URL.format(self.server_ip,
1626
1666
  filesystem_id),
1627
1667
  payload=None,
1628
- querystring=constants.SELECT_ALL_FILESYSTEM)
1668
+ querystring=querystring)
1629
1669
 
1630
1670
  def get_filesystem_by_name(self, filesystem_name, nas_server_id):
1631
1671
  """Get details of a filesystem by name.
@@ -1637,12 +1677,15 @@ class Provisioning:
1637
1677
  """
1638
1678
  LOG.info("Getting filesystem details by name: '%s' and NAS Server: "
1639
1679
  "'%s'" % (filesystem_name, nas_server_id))
1680
+ querystring=constants.SELECT_ALL_FILESYSTEM
1681
+ if helpers.is_foot_hill_prime_or_higher():
1682
+ querystring=constants.SELECT_ALL_FILESYSTEM_PRIME
1640
1683
  return self.client.request(
1641
1684
  constants.GET,
1642
1685
  constants.GET_FILESYSTEM_DETAILS_BY_NAME_URL.format(
1643
1686
  self.server_ip),
1644
1687
  payload=None, querystring=helpers.prepare_querystring(
1645
- constants.SELECT_ALL_FILESYSTEM,
1688
+ querystring,
1646
1689
  nas_server_id=constants.EQUALS + nas_server_id,
1647
1690
  name=constants.EQUALS + filesystem_name
1648
1691
  )
@@ -1671,6 +1714,10 @@ class Provisioning:
1671
1714
 
1672
1715
  if advance_parameters:
1673
1716
  for key, value in advance_parameters.items():
1717
+ if key in constants.FILESYSTEM_PRIME and \
1718
+ not helpers.is_foot_hill_prime_or_higher():
1719
+ raise Exception( key + " is supported for PowerStore" \
1720
+ " version 3.0.0.0 and above.")
1674
1721
  payload[key] = value
1675
1722
  return self.client.request(constants.POST,
1676
1723
  constants.CREATE_FILESYSTEM_URL.format(
@@ -1722,6 +1769,10 @@ class Provisioning:
1722
1769
  if modify_parameters:
1723
1770
  payload = dict()
1724
1771
  for key, value in modify_parameters.items():
1772
+ if key in constants.FILESYSTEM_PRIME and \
1773
+ not helpers.is_foot_hill_prime_or_higher():
1774
+ raise Exception( key + " is supported for PowerStore" \
1775
+ " version 3.0.0.0 and above.")
1725
1776
  if value is not None:
1726
1777
  payload[key] = value
1727
1778
 
@@ -60,16 +60,19 @@ FHP_VOLUME_DETAILS_QUERY = {
60
60
  }
61
61
 
62
62
  # Host Query
63
- SELECT_ALL_HOST = {"select": "id,name,description,os_type,"
64
- "host_group_id,"
65
- "host_initiators,os_type_l10n,"
66
- "mapped_hosts(id,logical_unit_number,"
67
- "host_group(id,name),volume(id,name))"
63
+ SELECT_ALL_HOST = {
64
+ "select": "id,name,description,os_type,"
65
+ "host_group_id,"
66
+ "host_initiators,os_type_l10n,"
67
+ "mapped_hosts(id,logical_unit_number,"
68
+ "host_group(id,name),volume(id,name)),"
69
+ "host_virtual_volume_mappings(id,host_id,host_group_id,virtual_volume_id)"
68
70
  }
69
71
  FHC_HOST_DETAILS_QUERY = {
70
72
  "select": "id,name,description,os_type,host_group_id,host_initiators,"
71
73
  "os_type_l10n,mapped_hosts(id,logical_unit_number,"
72
- "host_group(id,name),volume(id,name)),type,type_l10n"
74
+ "host_group(id,name),volume(id,name)),type,type_l10n,"
75
+ "host_virtual_volume_mappings(id,host_id,host_group_id,virtual_volume_id)"
73
76
  }
74
77
  FHP_HOST_DETAILS_QUERY = {
75
78
  "select": "id,name,description,type,os_type,host_group_id,"
@@ -77,11 +80,13 @@ FHP_HOST_DETAILS_QUERY = {
77
80
  "mapped_hosts(id,logical_unit_number,host_group(id,name),"
78
81
  "volume(id,name)),type_l10n,host_connectivity_l10n,"
79
82
  "initiators(id,port_name,port_type,chap_single_username,"
80
- "chap_mutual_username,active_sessions),host_initiators"
83
+ "chap_mutual_username,active_sessions),host_initiators,"
84
+ "host_virtual_volume_mappings(id,host_id,host_group_id,virtual_volume_id)"
81
85
  }
82
86
 
83
87
  SELECT_ALL_HOST_GROUP = {"select": "name,id,description,hosts(id,name)"}
84
- FHP_HOST_GROUP_QUERY = {"select": "name,id,description,hosts(id,name),"
88
+ FHP_HOST_GROUP_QUERY = {"select": "name,id,description,hosts(id,name,host_virtual_volume_mappings(id,"
89
+ "host_id,host_group_id,virtual_volume_id)),"
85
90
  "host_connectivity,host_connectivity_l10n,"
86
91
  "mapped_host_groups(id,volume_id,"
87
92
  "logical_unit_number)"}
@@ -136,6 +141,32 @@ SELECT_ALL_FILESYSTEM = {"select": "id,name, description,"
136
141
  "creator_type_l10n,nas_server(name,id),"
137
142
  "protection_policy(name,id)"}
138
143
 
144
+ SELECT_ALL_FILESYSTEM_PRIME = {"select": "id,name, description,"
145
+ "parent_id, filesystem_type, size_total,size_used,"
146
+ "access_policy,locking_policy,"
147
+ "folder_rename_policy, is_smb_sync_writes_enabled,"
148
+ "is_smb_op_locks_enabled, is_smb_no_notify_enabled,"
149
+ "is_smb_notify_on_access_enabled,"
150
+ "is_smb_notify_on_write_enabled,"
151
+ "smb_notify_on_change_dir_depth,"
152
+ "is_async_MTime_enabled, is_quota_enabled,"
153
+ "grace_period, default_hard_limit,"
154
+ "default_soft_limit, creation_timestamp,"
155
+ "expiration_timestamp, last_refresh_timestamp,"
156
+ "last_writable_timestamp, is_modified,access_type,"
157
+ "creator_type, filesystem_type_l10n,"
158
+ "access_policy_l10n, locking_policy_l10n,"
159
+ "folder_rename_policy_l10n, access_type_l10n,"
160
+ "creator_type_l10n,nas_server(name,id),"
161
+ "protection_policy(name,id),"
162
+ "file_events_publishing_mode,"
163
+ "file_events_publishing_mode_l10n,"
164
+ "config_type, config_type_l10n,flr_attributes,"
165
+ "host_io_size,host_io_size_l10n"}
166
+
167
+ FILESYSTEM_PRIME = ['config_type', 'is_async_MTime_enabled',
168
+ 'file_events_publishing_mode', 'flr_attributes',
169
+ 'host_io_size']
139
170
 
140
171
  FHP_NAS_QUERYSTRING = {"select": "id,name, description, operational_status,"
141
172
  "current_node_id,preferred_node_id,"
@@ -292,6 +323,20 @@ IP_PORT_DETAILS_QUERY = {
292
323
  VCENTER_DETAILS_QUERY = {
293
324
  'select': 'id,instance_uuid,address,username'
294
325
  }
326
+ FHC_MALKA_VCENTER_QUERY = {
327
+ 'select': 'id,instance_uuid,address,username,vendor_provider_status,'
328
+ 'vendor_provider_status_l10n,'
329
+ 'virtual_machines(id,name,instance_uuid,type,status,'
330
+ 'virtual_volumes,protection_policy_id)'
331
+ }
332
+ FHP_VCENTER_QUERY = {
333
+ 'select': 'id,instance_uuid,address,username,version,'
334
+ 'vendor_provider_status,vendor_provider_status_l10n,'
335
+ 'virtual_machines(id,name,instance_uuid,type,status,'
336
+ 'virtual_volumes,protection_policy_id),'
337
+ 'datastores(id,instance_uuid,name,type,'
338
+ 'storage_container_id),vsphere_hosts(id,name,version)'
339
+ }
295
340
 
296
341
  # Appliance details
297
342
  APPLIANCE_DETAILS_QUERY = {
@@ -322,16 +367,18 @@ REMOTE_SYSTEM_FHP_DETAILS_QUERY = {
322
367
  'state_l10n,data_connection_type_l10n,file_connection_state_l10n,'
323
368
  'data_connection_state_l10n,discovery_chap_mode_l10n,'
324
369
  'session_chap_mode_l10n,data_network_latency_l10n,'
325
- 'capabilities_l10n,storage_container_destinations'
370
+ 'capabilities_l10n,storage_container_destinations(id,'
371
+ 'storage_container_id,remote_system_id,remote_storage_container_id,'
372
+ 'storage_container(id,name))'
326
373
  }
327
374
 
328
375
  # Certificate details
329
376
  CERTIFICATE_DETAILS_QUERY = {
330
377
  'select': 'id,type,type_l10n,service,service_l10n,scope,is_current,'
331
- 'is_valid,members(subject,serial_number,signature_algorithm,'
332
- 'issuer,valid_from,valid_to,public_key_algorithm,key_length,'
333
- 'thumbprint_algorithm,thumbprint_algorithm_l10n,thumbprint,'
334
- 'certificate,depth,subject_alternative_names)'
378
+ 'is_valid,members(subject,serial_number,signature_algorithm,'
379
+ 'issuer,valid_from,valid_to,public_key_algorithm,key_length,'
380
+ 'thumbprint_algorithm,thumbprint_algorithm_l10n,thumbprint,'
381
+ 'certificate,depth,subject_alternative_names)'
335
382
  }
336
383
 
337
384
  # Security config details
@@ -378,6 +425,42 @@ LDAP_DOMAIN_DETAILS_QUERY = {
378
425
  'group_name_attribute,group_member_attribute,group_object_class,'
379
426
  'group_search_path,group_search_level,ldap_server_type_l10n,protocol_l10n'
380
427
  }
428
+ VIRTUAL_VOLUME_FHP_DETAILS_QUERY = {
429
+ 'select': 'id,name,size,type,usage_type,appliance_id,storage_container_id,io_priority,profile_id,'
430
+ 'replication_group_id,creator_type,is_readonly,migration_session_id,virtual_machine_uuid,'
431
+ 'family_id,parent_id,source_id,source_timestamp,creation_timestamp,naa_name,'
432
+ 'is_replication_destination,location_history,protection_policy_id,nsid,nguid,type_l10n,'
433
+ 'usage_type_l10n,io_priority_l10n,creator_type_l10n,'
434
+ 'host_virtual_volume_mappings(id,host_id,host_group_id,virtual_volume_id)'
435
+ }
436
+ VIRTUAL_VOLUME_DETAILS_QUERY = {
437
+ 'select': 'id,name,size,type,usage_type,appliance_id,storage_container_id,io_priority,profile_id,'
438
+ 'creator_type,is_readonly,migration_session_id,virtual_machine_uuid,'
439
+ 'family_id,parent_id,source_id,source_timestamp,creation_timestamp,'
440
+ 'location_history,type_l10n,usage_type_l10n,io_priority_l10n,creator_type_l10n,'
441
+ 'host_virtual_volume_mappings(id,host_id,host_group_id,virtual_volume_id)'
442
+ }
443
+
444
+ STORAGE_CONTAINER_DETAILS_QUERY = {
445
+ 'select': 'id,name,quota,storage_protocol,storage_protocol_l10n,'
446
+ 'virtual_volumes(id,name),replication_groups(id,name),datastores(id,name),'
447
+ 'destinations(id,remote_system_id,remote_storage_container_id)'
448
+ }
449
+
450
+ STORAGE_CONTAINER_DETAILS_DESTINATION_QUERY = {
451
+ 'select': 'id,remote_system_id,storage_container_id,'
452
+ 'storage_container(id,name),remote_storage_container_id,'
453
+ 'remote_system(id,name,management_address)'
454
+ }
455
+
456
+ REPLICATION_GROUP_QUERY = {
457
+ 'select': 'id,name,storage_container_id,description,creator_type,'
458
+ 'creation_timestamp,is_replication_destination,creator_type_l10n'
459
+ ',virtual_volumes,storage_container,parent,source,'
460
+ 'child_replication_groups,target_replication_groups,'
461
+ 'virtual_machines'
462
+ }
463
+
381
464
  # LDAP Account details
382
465
  LDAP_ACCOUNT_DETAILS_QUERY = {
383
466
  'select': 'id,role_id,domain_id,name,type,type_l10n,dn'
@@ -470,6 +553,10 @@ REPLICATION_SESSION_FAILOVER_URL = 'https://{0}/api/rest/replication_session/{1}
470
553
  REPLICATION_SESSION_REPROTECT_URL = 'https://{0}/api/rest/replication_session/{1}/reprotect'
471
554
  MODIFY_REPLICATION_SESSION_URL = REPLICATION_SESSION_OBJECT_URL
472
555
 
556
+ # Replication Group endpoints
557
+ REPLICATION_GROUP_DETAILS_LIST_URL = 'https://{0}/api/rest/replication_group'
558
+ REPLICATION_GROUP_DETAILS_URL = 'https://{0}/api/rest/replication_group/{1}'
559
+
473
560
  # Remote system endpoints
474
561
  GET_REMOTE_SYSTEM_LIST_URL = 'https://{0}/api/rest/remote_system'
475
562
  GET_REMOTE_SYSTEM_DETAILS_URL = 'https://{0}/api/rest/remote_system/{1}'
@@ -592,7 +679,9 @@ GET_JOB_DETAILS_URL = 'https://{0}/api/rest/job/{1}'
592
679
  # vCenter endpoints
593
680
  GET_VCENTER_LIST_URL = 'https://{0}/api/rest/vcenter'
594
681
  GET_VCENTER_DETAILS_URL = 'https://{0}/api/rest/vcenter/{1}'
682
+ ADD_VCENTER_URL = GET_VCENTER_LIST_URL
595
683
  MODIFY_VCENTER_URL = GET_VCENTER_DETAILS_URL
684
+ REMOVE_VCENTER_URL = GET_VCENTER_DETAILS_URL
596
685
 
597
686
  # Appliance endpoints
598
687
  GET_APPLIANCE_LIST_URL = 'https://{0}/api/rest/appliance'
@@ -667,3 +756,20 @@ GET_LDAP_ACCOUNT_DETAILS_URL = 'https://{0}/api/rest/ldap_account/{1}'
667
756
  CREATE_LDAP_ACCOUNT_URL = GET_LDAP_ACCOUNT_LIST_URL
668
757
  MODIFY_LDAP_ACCOUNT_URL = GET_LDAP_ACCOUNT_DETAILS_URL
669
758
  DELETE_LDAP_ACCOUNT_URL = GET_LDAP_ACCOUNT_DETAILS_URL
759
+
760
+ # virtual volume endpoints
761
+ GET_VIRTUAL_VOLUME_LIST_URL = 'https://{0}/api/rest/virtual_volume'
762
+ GET_VIRTUAL_VOLUME_DETAILS_URL = 'https://{0}/api/rest/virtual_volume/{1}'
763
+
764
+ # Storage container endpoints
765
+ GET_STORAGE_CONTAINER_LIST_URL = 'https://{0}/api/rest/storage_container'
766
+ GET_STORAGE_CONTAINER_DETAILS_URL = 'https://{0}/api/rest/storage_container/{1}'
767
+ CREATE_STORAGE_CONTAINER_URL = GET_STORAGE_CONTAINER_LIST_URL
768
+ MODIFY_STORAGE_CONTAINER_URL = GET_STORAGE_CONTAINER_DETAILS_URL
769
+ DELETE_STORAGE_CONTAINER_URL = GET_STORAGE_CONTAINER_DETAILS_URL
770
+
771
+ # Storage container Destination endpoints
772
+ GET_STORAGE_CONTAINER_DESTINATION_LIST_URL = "https://{0}/api/rest/storage_container_destination"
773
+ GET_STORAGE_CONTAINER_DESTINATION_DETAILS_URL = "https://{0}/api/rest/storage_container_destination/{1}"
774
+ CREATE_STORAGE_CONTAINER_DESTINATION_URL = GET_STORAGE_CONTAINER_DESTINATION_LIST_URL
775
+ DELETE_STORAGE_CONTAINER_DESTINATION_URL = GET_STORAGE_CONTAINER_DESTINATION_DETAILS_URL
@@ -48,7 +48,7 @@ def get_logger(module_name, enable_log=False):
48
48
 
49
49
 
50
50
  def is_foot_hill_or_higher():
51
- """Return a true if the array version is foot hill or higher.
51
+ """Returns true if the array version is foot hill or higher.
52
52
 
53
53
  :return: True if foot hill or higher
54
54
  :rtype: bool
@@ -60,9 +60,21 @@ def is_foot_hill_or_higher():
60
60
  return True
61
61
  return False
62
62
 
63
+ def is_malka_or_higher():
64
+ """Returns true if the array version is Malka or higher.
65
+
66
+ :return: True if array version is Malka or higher
67
+ :rtype: bool
68
+ """
69
+ malka_version = '2.1.0.0'
70
+ array_version = provisioning_obj.get_array_version()
71
+ if array_version and (
72
+ parse_version(array_version[0:7]) >= parse_version(malka_version)):
73
+ return True
74
+ return False
63
75
 
64
76
  def is_foot_hill_prime_or_higher():
65
- """Return a true if the array version is foothill prime or higher.
77
+ """Returns true if the array version is foothill prime or higher.
66
78
 
67
79
  :return: True if foothill prime or higher
68
80
  :rtype: bool
@@ -1,6 +1,6 @@
1
- Metadata-Version: 1.0
1
+ Metadata-Version: 1.1
2
2
  Name: PyPowerStore
3
- Version: 1.9.0.0
3
+ Version: 2.0.0.0
4
4
  Summary: Python Library for Dell PowerStore
5
5
  Home-page: https://github.com/dell/python-powerstore
6
6
  Author: Ansible Team at Dell
@@ -8,3 +8,4 @@ Author-email: ansible.team@dell.com
8
8
  License: UNKNOWN
9
9
  Description: UNKNOWN
10
10
  Platform: UNKNOWN
11
+ Classifier: License :: OSI Approved :: Apache Software License
@@ -1,4 +1,3 @@
1
- LICENSE
2
1
  README.md
3
2
  setup.py
4
3
  PyPowerStore/__init__.py
@@ -46,7 +46,7 @@ The library docs are available under 'docs' folder.
46
46
 
47
47
  This library uses python's "requests" library.
48
48
 
49
- PyPowerStore officially supports Python 3.8, 3.9 and 3.10.
49
+ PyPowerStore officially supports Python 3.9, 3.10 and 3.11.
50
50
 
51
51
 
52
52
  ## Support
@@ -10,7 +10,7 @@ except ImportError:
10
10
 
11
11
 
12
12
  setup(name='PyPowerStore',
13
- version='1.9.0.0',
13
+ version='2.0.0.0',
14
14
  description='Python Library for Dell PowerStore',
15
15
  author='Ansible Team at Dell',
16
16
  author_email='ansible.team@dell.com',
@@ -18,6 +18,8 @@ setup(name='PyPowerStore',
18
18
  'urllib3>=1.26.7',
19
19
  'requests>=2.23.0'
20
20
  ],
21
+ license_files = ('LICENSE',),
22
+ classifiers=['License :: OSI Approved :: Apache Software License'],
21
23
  url='https://github.com/dell/python-powerstore',
22
24
  packages=['PyPowerStore', 'PyPowerStore.utils'],
23
25
  )
@@ -1,201 +0,0 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- http://www.apache.org/licenses/
4
-
5
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
-
7
- 1. Definitions.
8
-
9
- "License" shall mean the terms and conditions for use, reproduction,
10
- and distribution as defined by Sections 1 through 9 of this document.
11
-
12
- "Licensor" shall mean the copyright owner or entity authorized by
13
- the copyright owner that is granting the License.
14
-
15
- "Legal Entity" shall mean the union of the acting entity and all
16
- other entities that control, are controlled by, or are under common
17
- control with that entity. For the purposes of this definition,
18
- "control" means (i) the power, direct or indirect, to cause the
19
- direction or management of such entity, whether by contract or
20
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
- outstanding shares, or (iii) beneficial ownership of such entity.
22
-
23
- "You" (or "Your") shall mean an individual or Legal Entity
24
- exercising permissions granted by this License.
25
-
26
- "Source" form shall mean the preferred form for making modifications,
27
- including but not limited to software source code, documentation
28
- source, and configuration files.
29
-
30
- "Object" form shall mean any form resulting from mechanical
31
- transformation or translation of a Source form, including but
32
- not limited to compiled object code, generated documentation,
33
- and conversions to other media types.
34
-
35
- "Work" shall mean the work of authorship, whether in Source or
36
- Object form, made available under the License, as indicated by a
37
- copyright notice that is included in or attached to the work
38
- (an example is provided in the Appendix below).
39
-
40
- "Derivative Works" shall mean any work, whether in Source or Object
41
- form, that is based on (or derived from) the Work and for which the
42
- editorial revisions, annotations, elaborations, or other modifications
43
- represent, as a whole, an original work of authorship. For the purposes
44
- of this License, Derivative Works shall not include works that remain
45
- separable from, or merely link (or bind by name) to the interfaces of,
46
- the Work and Derivative Works thereof.
47
-
48
- "Contribution" shall mean any work of authorship, including
49
- the original version of the Work and any modifications or additions
50
- to that Work or Derivative Works thereof, that is intentionally
51
- submitted to Licensor for inclusion in the Work by the copyright owner
52
- or by an individual or Legal Entity authorized to submit on behalf of
53
- the copyright owner. For the purposes of this definition, "submitted"
54
- means any form of electronic, verbal, or written communication sent
55
- to the Licensor or its representatives, including but not limited to
56
- communication on electronic mailing lists, source code control systems,
57
- and issue tracking systems that are managed by, or on behalf of, the
58
- Licensor for the purpose of discussing and improving the Work, but
59
- excluding communication that is conspicuously marked or otherwise
60
- designated in writing by the copyright owner as "Not a Contribution."
61
-
62
- "Contributor" shall mean Licensor and any individual or Legal Entity
63
- on behalf of whom a Contribution has been received by Licensor and
64
- subsequently incorporated within the Work.
65
-
66
- 2. Grant of Copyright License. Subject to the terms and conditions of
67
- this License, each Contributor hereby grants to You a perpetual,
68
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
- copyright license to reproduce, prepare Derivative Works of,
70
- publicly display, publicly perform, sublicense, and distribute the
71
- Work and such Derivative Works in Source or Object form.
72
-
73
- 3. Grant of Patent License. Subject to the terms and conditions of
74
- this License, each Contributor hereby grants to You a perpetual,
75
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
- (except as stated in this section) patent license to make, have made,
77
- use, offer to sell, sell, import, and otherwise transfer the Work,
78
- where such license applies only to those patent claims licensable
79
- by such Contributor that are necessarily infringed by their
80
- Contribution(s) alone or by combination of their Contribution(s)
81
- with the Work to which such Contribution(s) was submitted. If You
82
- institute patent litigation against any entity (including a
83
- cross-claim or counterclaim in a lawsuit) alleging that the Work
84
- or a Contribution incorporated within the Work constitutes direct
85
- or contributory patent infringement, then any patent licenses
86
- granted to You under this License for that Work shall terminate
87
- as of the date such litigation is filed.
88
-
89
- 4. Redistribution. You may reproduce and distribute copies of the
90
- Work or Derivative Works thereof in any medium, with or without
91
- modifications, and in Source or Object form, provided that You
92
- meet the following conditions:
93
-
94
- (a) You must give any other recipients of the Work or
95
- Derivative Works a copy of this License; and
96
-
97
- (b) You must cause any modified files to carry prominent notices
98
- stating that You changed the files; and
99
-
100
- (c) You must retain, in the Source form of any Derivative Works
101
- that You distribute, all copyright, patent, trademark, and
102
- attribution notices from the Source form of the Work,
103
- excluding those notices that do not pertain to any part of
104
- the Derivative Works; and
105
-
106
- (d) If the Work includes a "NOTICE" text file as part of its
107
- distribution, then any Derivative Works that You distribute must
108
- include a readable copy of the attribution notices contained
109
- within such NOTICE file, excluding those notices that do not
110
- pertain to any part of the Derivative Works, in at least one
111
- of the following places: within a NOTICE text file distributed
112
- as part of the Derivative Works; within the Source form or
113
- documentation, if provided along with the Derivative Works; or,
114
- within a display generated by the Derivative Works, if and
115
- wherever such third-party notices normally appear. The contents
116
- of the NOTICE file are for informational purposes only and
117
- do not modify the License. You may add Your own attribution
118
- notices within Derivative Works that You distribute, alongside
119
- or as an addendum to the NOTICE text from the Work, provided
120
- that such additional attribution notices cannot be construed
121
- as modifying the License.
122
-
123
- You may add Your own copyright statement to Your modifications and
124
- may provide additional or different license terms and conditions
125
- for use, reproduction, or distribution of Your modifications, or
126
- for any such Derivative Works as a whole, provided Your use,
127
- reproduction, and distribution of the Work otherwise complies with
128
- the conditions stated in this License.
129
-
130
- 5. Submission of Contributions. Unless You explicitly state otherwise,
131
- any Contribution intentionally submitted for inclusion in the Work
132
- by You to the Licensor shall be under the terms and conditions of
133
- this License, without any additional terms or conditions.
134
- Notwithstanding the above, nothing herein shall supersede or modify
135
- the terms of any separate license agreement you may have executed
136
- with Licensor regarding such Contributions.
137
-
138
- 6. Trademarks. This License does not grant permission to use the trade
139
- names, trademarks, service marks, or product names of the Licensor,
140
- except as required for reasonable and customary use in describing the
141
- origin of the Work and reproducing the content of the NOTICE file.
142
-
143
- 7. Disclaimer of Warranty. Unless required by applicable law or
144
- agreed to in writing, Licensor provides the Work (and each
145
- Contributor provides its Contributions) on an "AS IS" BASIS,
146
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
- implied, including, without limitation, any warranties or conditions
148
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
- PARTICULAR PURPOSE. You are solely responsible for determining the
150
- appropriateness of using or redistributing the Work and assume any
151
- risks associated with Your exercise of permissions under this License.
152
-
153
- 8. Limitation of Liability. In no event and under no legal theory,
154
- whether in tort (including negligence), contract, or otherwise,
155
- unless required by applicable law (such as deliberate and grossly
156
- negligent acts) or agreed to in writing, shall any Contributor be
157
- liable to You for damages, including any direct, indirect, special,
158
- incidental, or consequential damages of any character arising as a
159
- result of this License or out of the use or inability to use the
160
- Work (including but not limited to damages for loss of goodwill,
161
- work stoppage, computer failure or malfunction, or any and all
162
- other commercial damages or losses), even if such Contributor
163
- has been advised of the possibility of such damages.
164
-
165
- 9. Accepting Warranty or Additional Liability. While redistributing
166
- the Work or Derivative Works thereof, You may choose to offer,
167
- and charge a fee for, acceptance of support, warranty, indemnity,
168
- or other liability obligations and/or rights consistent with this
169
- License. However, in accepting such obligations, You may act only
170
- on Your own behalf and on Your sole responsibility, not on behalf
171
- of any other Contributor, and only if You agree to indemnify,
172
- defend, and hold each Contributor harmless for any liability
173
- incurred by, or claims asserted against, such Contributor by reason
174
- of your accepting any such warranty or additional liability.
175
-
176
- END OF TERMS AND CONDITIONS
177
-
178
- APPENDIX: How to apply the Apache License to your work.
179
-
180
- To apply the Apache License to your work, attach the following
181
- boilerplate notice, with the fields enclosed by brackets "[]"
182
- replaced with your own identifying information. (Don't include
183
- the brackets!) The text should be enclosed in the appropriate
184
- comment syntax for the file format. We also recommend that a
185
- file or class name and description of purpose be included on the
186
- same "printed page" as the copyright notice for easier
187
- identification within third-party archives.
188
-
189
- Copyright [yyyy] [name of copyright owner]
190
-
191
- Licensed under the Apache License, Version 2.0 (the "License");
192
- you may not use this file except in compliance with the License.
193
- You may obtain a copy of the License at
194
-
195
- http://www.apache.org/licenses/LICENSE-2.0
196
-
197
- Unless required by applicable law or agreed to in writing, software
198
- distributed under the License is distributed on an "AS IS" BASIS,
199
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
- See the License for the specific language governing permissions and
201
- limitations under the License.
File without changes