Skip to content
This repository was archived by the owner on Mar 6, 2024. It is now read-only.

Commit 17914bc

Browse files
authored
Add support to escape special characters in query filter for api v35.0+ (#711)
For api v35.0+, vCD requires special characters viz. ( ) ; , to be escaped if used as value in query filter. In _AbstractQuery, we escape the special characters in encoded filter value. Additional changes, * Replaced usage of quote_plus to quote, since quote_plus will replace spaces with + and thus can cause the filter value to distort. * Encoded all usage of qfilter in get_typed_query, since this field should be encoded by caller.
1 parent e900911 commit 17914bc

9 files changed

Lines changed: 49 additions & 18 deletions

File tree

pyvcloud/vcd/api_extension.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ def _get_extension_record(self,
8282
:raise MultipleRecordsException: if more than one service with the
8383
given name and namespace are found.
8484
"""
85-
qfilter = 'name==%s' % urllib.parse.quote_plus(name)
85+
qfilter = 'name==%s' % urllib.parse.quote(name)
8686
if namespace is not None:
87-
qfilter += ';namespace==%s' % urllib.parse.quote_plus(namespace)
87+
qfilter += ';namespace==%s' % urllib.parse.quote(namespace)
8888
try:
8989
ext = self.client.get_typed_query(
9090
ResourceType.ADMIN_SERVICE.value,

pyvcloud/vcd/client.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,24 +1827,50 @@ def __init__(self,
18271827
self._include_links = include_links
18281828
self._page = 1
18291829

1830-
self._filter = qfilter
1831-
if equality_filter is not None:
1832-
if self._filter is not None:
1830+
is_below_v35_query = float(client.get_api_version()) < float(ApiVersion.VERSION_35.value) # noqa: E501
1831+
if is_below_v35_query:
1832+
self._filter = qfilter
1833+
else:
1834+
self._filter = self._escape_special_characters(qfilter)
1835+
1836+
if equality_filter:
1837+
if self._filter:
18331838
self._filter += ';'
18341839
else:
18351840
self._filter = ''
18361841
self._filter += equality_filter[0]
18371842
self._filter += '=='
1838-
if float(client.get_api_version()) < float(ApiVersion.VERSION_35.value): # noqa: E501
1843+
if is_below_v35_query:
18391844
self._filter += equality_filter[1]
18401845
else:
1841-
self._filter += urllib.parse.quote(equality_filter[1])
1846+
self._filter += self._escape_special_characters(
1847+
urllib.parse.quote(equality_filter[1]))
18421848

18431849
self._sort_desc = sort_desc
18441850
self._sort_asc = sort_asc
18451851

18461852
self.fields = fields
18471853

1854+
def _escape_special_characters(self, single_encoded_value_string):
1855+
"""Escape vCD query specific special characters viz. ( ) ; ,.
1856+
1857+
This method accepts a url encoded string and replaces encoded
1858+
instances of ( ) ; , with their escaped version.
1859+
1860+
:param str single_encoded_value_string: string to be processed
1861+
1862+
:return: string with vCD query specific special characters escaped
1863+
1864+
:rtype: str
1865+
"""
1866+
val = single_encoded_value_string
1867+
if val:
1868+
val = val.replace('%28', '%5C%28')
1869+
val = val.replace('%29', '%5C%29')
1870+
val = val.replace('%2C', '%5C%2C')
1871+
val = val.replace('%3B', '%5C%3B')
1872+
return val
1873+
18481874
def execute(self):
18491875
"""Executes query and returns results.
18501876

pyvcloud/vcd/external_network.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
import urllib
17+
1618
from pyvcloud.vcd.client import E
1719
from pyvcloud.vcd.client import E_VMEXT
1820
from pyvcloud.vcd.client import EntityType
@@ -562,7 +564,7 @@ def list_associated_direct_org_vdc_networks(self, filter=None):
562564
:raises: EntityNotFoundException: if any direct org vDC network
563565
cannot be found.
564566
"""
565-
query_filter = 'connectedTo==' + self.name
567+
query_filter = 'connectedTo==' + urllib.parse.quote(self.name)
566568
if filter:
567569
query_filter += ';' + filter
568570
query = self.client.get_typed_query(
@@ -585,7 +587,7 @@ def list_vsphere_network(self, filter=None):
585587
:rtype: list
586588
"""
587589
out_list = []
588-
query_filter = 'networkName==' + self.name
590+
query_filter = 'networkName==' + urllib.parse.quote(self.name)
589591
if filter:
590592
query_filter += ';' + filter
591593
query = self.client.get_typed_query(

pyvcloud/vcd/org.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,7 @@ def list_users(self, name_filter=None):
10591059
if self.client.is_sysadmin():
10601060
resource_type = ResourceType.ADMIN_USER.value
10611061
org_filter = 'org==%s' % \
1062-
urllib.parse.quote_plus(self.resource.get('href'))
1062+
urllib.parse.quote(self.resource.get('href'))
10631063
query = self.client.get_typed_query(
10641064
resource_type,
10651065
query_result_format=QueryResultFormat.RECORDS,
@@ -1168,7 +1168,7 @@ def list_roles(self, name_filter=None):
11681168
if self.client.is_sysadmin():
11691169
resource_type = ResourceType.ADMIN_ROLE.value
11701170
org_filter = 'org==%s' % \
1171-
urllib.parse.quote_plus(self.resource.get('href'))
1171+
urllib.parse.quote(self.resource.get('href'))
11721172

11731173
query = self.client.get_typed_query(
11741174
resource_type,

pyvcloud/vcd/platform.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ def get_port_group_moref_types(self, vim_server_name, port_group_name):
10131013
:raises: EntityNotFoundException: if any port group name cannot be
10141014
found.
10151015
"""
1016-
vcfilter = 'vcName==%s' % urllib.parse.quote_plus(vim_server_name)
1016+
vcfilter = 'vcName==%s' % urllib.parse.quote(vim_server_name)
10171017
query = self.client.get_typed_query(
10181018
ResourceType.PORT_GROUP.value,
10191019
qfilter=vcfilter,
@@ -1041,7 +1041,7 @@ def list_available_port_group_names(self, vim_server_name):
10411041
:raises: EntityNotFoundException: if any port group name cannot be
10421042
found.
10431043
"""
1044-
vcfilter = 'vcName==%s' % urllib.parse.quote_plus(vim_server_name)
1044+
vcfilter = 'vcName==%s' % urllib.parse.quote(vim_server_name)
10451045
query = self.client.get_typed_query(
10461046
ResourceType.PORT_GROUP.value,
10471047
qfilter=vcfilter,

pyvcloud/vcd/system.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from pyvcloud.vcd.client import NSMAP
1919
from pyvcloud.vcd.client import QueryResultFormat
2020
from pyvcloud.vcd.client import RelationType
21+
from pyvcloud.vcd.client import ResourceType
2122
from pyvcloud.vcd.exceptions import EntityNotFoundException
2223
from pyvcloud.vcd.exceptions import InvalidParameterException
2324
from pyvcloud.vcd.utils import get_admin_href
@@ -129,7 +130,7 @@ def list_provider_vdc_storage_profiles(self, name=None):
129130
name_filter = ('name', name)
130131

131132
q = self.client.get_typed_query(
132-
'providerVdcStorageProfile',
133+
ResourceType.PROVIDER_VDC_STORAGE_PROFILE.value,
133134
query_result_format=QueryResultFormat.RECORDS,
134135
equality_filter=name_filter)
135136

pyvcloud/vcd/task.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def list_tasks(self,
125125
"""
126126
query_filter = ''
127127
for f in filter_status_list:
128-
query_filter += 'status==%s,' % urllib.parse.quote_plus(f)
128+
query_filter += 'status==%s,' % urllib.parse.quote(f)
129129
if len(query_filter) > 0:
130130
query_filter = query_filter[:-1]
131131
sort_asc = None

pyvcloud/vcd/vdc.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import urllib
16+
1517
from lxml import etree
1618

1719
from pyvcloud.vcd.acl import Acl
@@ -155,7 +157,7 @@ def query_vm_by_name(self, name):
155157
:raises: MultipleRecordsException: if more than one VM with the
156158
provided name are found.
157159
"""
158-
vdc_filter = ('vdc==%s' % self.href)
160+
vdc_filter = ('vdc==%s' % urllib.parse.quote(self.href))
159161
name_filter = ('name', name)
160162
query_obj = self.client.get_typed_query(
161163
ResourceType.ADMIN_VM.value,
@@ -166,7 +168,7 @@ def query_vm_by_name(self, name):
166168

167169
def get_vapp_href(self, name):
168170
name_filter = ('name', name)
169-
vdc_filter = 'vdc==%s' % self.href
171+
vdc_filter = 'vdc==%s' % urllib.parse.quote(self.href)
170172
resource_type = ResourceType.VAPP.value
171173
if self.client.is_sysadmin():
172174
resource_type = ResourceType.ADMIN_VAPP.value

system_tests/nsxt_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_0010_register_nsxt(self):
3434
platform = Platform(TestNSXT._client)
3535

3636
manager_name = Environment._config['nsxt']['manager_name']
37-
query_filter = 'name==%s' % urllib.parse.quote_plus(manager_name)
37+
query_filter = 'name==%s' % urllib.parse.quote(manager_name)
3838
query = TestNSXT._client.get_typed_query(
3939
ResourceType.NSXT_MANAGER.value,
4040
query_result_format=QueryResultFormat.REFERENCES,

0 commit comments

Comments
 (0)