Fix Tempest microversion comparison approach
Manila microversions have following template: x.y where 'x' and 'y' both digits. And now tempest transforms string 'x.y' to float but it is incorrect thing to do because float assumes that each left value is bigger than right one. And it is not suitable for microversion comparisons. Examples: Microversions true conditions: 2.9 < 2.10 2.9 < 2.81 Float true conditions: 2.9 > 2.10 2.9 > 2.81 So, create new file 'manila_tempest_tests/utils.py' and place there old and new functions that serve all microversion actions. In addition, port another existing utility function called 'rand_ip'. Change-Id: I88bf2cb51fd8de1bc89bf169bda7a05ca5a0b8ab Closes-Bug: #1518996
This commit is contained in:
parent
51069d5390
commit
afeb366fd3
@ -17,12 +17,13 @@ import json
|
|||||||
import time
|
import time
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
|
from tempest import config
|
||||||
from tempest_lib.common.utils import data_utils
|
from tempest_lib.common.utils import data_utils
|
||||||
from tempest_lib import exceptions
|
from tempest_lib import exceptions
|
||||||
|
|
||||||
from manila_tempest_tests.services.share.json import shares_client # noqa
|
from manila_tempest_tests.services.share.json import shares_client
|
||||||
from manila_tempest_tests import share_exceptions
|
from manila_tempest_tests import share_exceptions
|
||||||
from tempest import config # noqa
|
from manila_tempest_tests import utils
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
LATEST_MICROVERSION = CONF.share.max_api_microversion
|
LATEST_MICROVERSION = CONF.share.max_api_microversion
|
||||||
@ -105,7 +106,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
cgsnapshots.
|
cgsnapshots.
|
||||||
"""
|
"""
|
||||||
if action_name is None:
|
if action_name is None:
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
action_name = 'reset_status'
|
action_name = 'reset_status'
|
||||||
else:
|
else:
|
||||||
action_name = 'os-reset_status'
|
action_name = 'os-reset_status'
|
||||||
@ -124,7 +125,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
s_type: shares, snapshots
|
s_type: shares, snapshots
|
||||||
"""
|
"""
|
||||||
if action_name is None:
|
if action_name is None:
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
action_name = 'force_delete'
|
action_name = 'force_delete'
|
||||||
else:
|
else:
|
||||||
action_name = 'os-force_delete'
|
action_name = 'os-force_delete'
|
||||||
@ -292,7 +293,10 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
def extend_share(self, share_id, new_size, version=LATEST_MICROVERSION,
|
def extend_share(self, share_id, new_size, version=LATEST_MICROVERSION,
|
||||||
action_name=None):
|
action_name=None):
|
||||||
if action_name is None:
|
if action_name is None:
|
||||||
action_name = 'extend' if float(version) > 2.6 else 'os-extend'
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
|
action_name = 'extend'
|
||||||
|
else:
|
||||||
|
action_name = 'os-extend'
|
||||||
post_body = {
|
post_body = {
|
||||||
action_name: {
|
action_name: {
|
||||||
"new_size": new_size,
|
"new_size": new_size,
|
||||||
@ -307,7 +311,10 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
def shrink_share(self, share_id, new_size, version=LATEST_MICROVERSION,
|
def shrink_share(self, share_id, new_size, version=LATEST_MICROVERSION,
|
||||||
action_name=None):
|
action_name=None):
|
||||||
if action_name is None:
|
if action_name is None:
|
||||||
action_name = 'shrink' if float(version) > 2.6 else 'os-shrnk'
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
|
action_name = 'shrink'
|
||||||
|
else:
|
||||||
|
action_name = 'os-shrink'
|
||||||
post_body = {
|
post_body = {
|
||||||
action_name: {
|
action_name: {
|
||||||
"new_size": new_size,
|
"new_size": new_size,
|
||||||
@ -337,7 +344,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if url is None:
|
if url is None:
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
url = 'shares/manage'
|
url = 'shares/manage'
|
||||||
else:
|
else:
|
||||||
url = 'os-share-manage'
|
url = 'os-share-manage'
|
||||||
@ -349,10 +356,16 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
def unmanage_share(self, share_id, version=LATEST_MICROVERSION, url=None,
|
def unmanage_share(self, share_id, version=LATEST_MICROVERSION, url=None,
|
||||||
action_name=None, body=None):
|
action_name=None, body=None):
|
||||||
if url is None:
|
if url is None:
|
||||||
url = 'shares' if float(version) > 2.6 else 'os-share-unmanage'
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
|
url = 'shares'
|
||||||
|
else:
|
||||||
|
url = 'os-share-unmanage'
|
||||||
if action_name is None:
|
if action_name is None:
|
||||||
action_name = 'action' if float(version) > 2.6 else 'unmanage'
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
if body is None and float(version) > 2.6:
|
action_name = 'action'
|
||||||
|
else:
|
||||||
|
action_name = 'unmanage'
|
||||||
|
if body is None and utils.is_microversion_gt(version, "2.6"):
|
||||||
body = json.dumps({'unmanage': {}})
|
body = json.dumps({'unmanage': {}})
|
||||||
resp, body = self.post(
|
resp, body = self.post(
|
||||||
"%(url)s/%(share_id)s/%(action_name)s" % {
|
"%(url)s/%(share_id)s/%(action_name)s" % {
|
||||||
@ -365,7 +378,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
###############
|
###############
|
||||||
|
|
||||||
def _get_access_action_name(self, version):
|
def _get_access_action_name(self, version):
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
return 'allow_access'
|
return 'allow_access'
|
||||||
return 'os-allow_access'
|
return 'os-allow_access'
|
||||||
|
|
||||||
@ -412,7 +425,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
version=LATEST_MICROVERSION):
|
version=LATEST_MICROVERSION):
|
||||||
"""Get list of availability zones."""
|
"""Get list of availability zones."""
|
||||||
if url is None:
|
if url is None:
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
url = 'availability-zones'
|
url = 'availability-zones'
|
||||||
else:
|
else:
|
||||||
url = 'os-availability-zone'
|
url = 'os-availability-zone'
|
||||||
@ -426,7 +439,10 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
version=LATEST_MICROVERSION):
|
version=LATEST_MICROVERSION):
|
||||||
"""List services."""
|
"""List services."""
|
||||||
if url is None:
|
if url is None:
|
||||||
url = 'services' if float(version) > 2.6 else 'os-services'
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
|
url = 'services'
|
||||||
|
else:
|
||||||
|
url = 'os-services'
|
||||||
if params:
|
if params:
|
||||||
url += '?%s' % urllib.urlencode(params)
|
url += '?%s' % urllib.urlencode(params)
|
||||||
resp, body = self.get(url, version=version)
|
resp, body = self.get(url, version=version)
|
||||||
@ -445,7 +461,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
|
|
||||||
def create_share_type(self, name, is_public=True,
|
def create_share_type(self, name, is_public=True,
|
||||||
version=LATEST_MICROVERSION, **kwargs):
|
version=LATEST_MICROVERSION, **kwargs):
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
is_public_keyname = 'share_type_access:is_public'
|
is_public_keyname = 'share_type_access:is_public'
|
||||||
else:
|
else:
|
||||||
is_public_keyname = 'os-share-type-access:is_public'
|
is_public_keyname = 'os-share-type-access:is_public'
|
||||||
@ -473,7 +489,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
version=LATEST_MICROVERSION,
|
version=LATEST_MICROVERSION,
|
||||||
action_name=None):
|
action_name=None):
|
||||||
if action_name is None:
|
if action_name is None:
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
action_name = 'share_type_access'
|
action_name = 'share_type_access'
|
||||||
else:
|
else:
|
||||||
action_name = 'os-share-type-access'
|
action_name = 'os-share-type-access'
|
||||||
@ -487,7 +503,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
###############
|
###############
|
||||||
|
|
||||||
def _get_quotas_url(self, version):
|
def _get_quotas_url(self, version):
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
return 'quota-sets'
|
return 'quota-sets'
|
||||||
return 'os-quota-sets'
|
return 'os-quota-sets'
|
||||||
|
|
||||||
@ -758,7 +774,7 @@ class SharesV2Client(shares_client.SharesClient):
|
|||||||
def migrate_share(self, share_id, host, version=LATEST_MICROVERSION,
|
def migrate_share(self, share_id, host, version=LATEST_MICROVERSION,
|
||||||
action_name=None):
|
action_name=None):
|
||||||
if action_name is None:
|
if action_name is None:
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
action_name = 'migrate_share'
|
action_name = 'migrate_share'
|
||||||
else:
|
else:
|
||||||
action_name = 'os-migrate_share'
|
action_name = 'os-migrate_share'
|
||||||
|
@ -14,13 +14,14 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from tempest import config # noqa
|
from tempest import config
|
||||||
from tempest import test # noqa
|
from tempest import test
|
||||||
from tempest_lib.common.utils import data_utils # noqa
|
from tempest_lib.common.utils import data_utils
|
||||||
from tempest_lib import exceptions as lib_exc # noqa
|
from tempest_lib import exceptions as lib_exc
|
||||||
import testtools # noqa
|
import testtools
|
||||||
|
|
||||||
from manila_tempest_tests.tests.api import base
|
from manila_tempest_tests.tests.api import base
|
||||||
|
from manila_tempest_tests import utils
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
|
|||||||
|
|
||||||
# Data for creating shares in parallel
|
# Data for creating shares in parallel
|
||||||
data = [creation_data, creation_data]
|
data = [creation_data, creation_data]
|
||||||
if float(CONF.share.max_api_microversion) >= 2.8:
|
if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.8"):
|
||||||
data.append(creation_data)
|
data.append(creation_data)
|
||||||
shares_created = cls.create_shares(data)
|
shares_created = cls.create_shares(data)
|
||||||
|
|
||||||
@ -127,7 +128,7 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
|
|||||||
share = self.shares_v2_client.get_share(share['id'], version="2.6")
|
share = self.shares_v2_client.get_share(share['id'], version="2.6")
|
||||||
self.assertEqual(self.st['share_type']['id'], share['share_type'])
|
self.assertEqual(self.st['share_type']['id'], share['share_type'])
|
||||||
|
|
||||||
if float(version) >= 2.8:
|
if utils.is_microversion_ge(version, "2.8"):
|
||||||
self.assertEqual(is_public, share['is_public'])
|
self.assertEqual(is_public, share['is_public'])
|
||||||
else:
|
else:
|
||||||
self.assertFalse(share['is_public'])
|
self.assertFalse(share['is_public'])
|
||||||
@ -139,9 +140,7 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
|
|||||||
self.shares_v2_client.get_share,
|
self.shares_v2_client.get_share,
|
||||||
share['id'])
|
share['id'])
|
||||||
|
|
||||||
@testtools.skipIf(
|
@base.skip_if_microversion_not_supported("2.8")
|
||||||
float(CONF.share.max_api_microversion) < 2.8,
|
|
||||||
"Only for API Microversion >= 2.8")
|
|
||||||
@test.attr(type=["gate", "smoke"])
|
@test.attr(type=["gate", "smoke"])
|
||||||
def test_manage_with_is_public_True(self):
|
def test_manage_with_is_public_True(self):
|
||||||
self._test_manage(share=self.shares[2], is_public=True)
|
self._test_manage(share=self.shares[2], is_public=True)
|
||||||
|
@ -20,7 +20,7 @@ from tempest_lib.common.utils import data_utils # noqa
|
|||||||
from tempest_lib import exceptions as lib_exc # noqa
|
from tempest_lib import exceptions as lib_exc # noqa
|
||||||
|
|
||||||
from manila_tempest_tests.tests.api import base
|
from manila_tempest_tests.tests.api import base
|
||||||
|
from manila_tempest_tests import utils
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ class ShareTypesAdminTest(base.BaseSharesAdminTest):
|
|||||||
def _verify_is_public_key_name(self, share_type, version):
|
def _verify_is_public_key_name(self, share_type, version):
|
||||||
old_key_name = 'os-share-type-access:is_public'
|
old_key_name = 'os-share-type-access:is_public'
|
||||||
new_key_name = 'share_type_access:is_public'
|
new_key_name = 'share_type_access:is_public'
|
||||||
if float(version) > 2.6:
|
if utils.is_microversion_gt(version, "2.6"):
|
||||||
self.assertIn(new_key_name, share_type)
|
self.assertIn(new_key_name, share_type)
|
||||||
self.assertNotIn(old_key_name, share_type)
|
self.assertNotIn(old_key_name, share_type)
|
||||||
else:
|
else:
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
import random
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
@ -27,27 +26,15 @@ from tempest import config
|
|||||||
from tempest import test
|
from tempest import test
|
||||||
from tempest_lib.common.utils import data_utils
|
from tempest_lib.common.utils import data_utils
|
||||||
from tempest_lib import exceptions
|
from tempest_lib import exceptions
|
||||||
import testtools
|
|
||||||
|
|
||||||
from manila_tempest_tests import clients_share as clients
|
from manila_tempest_tests import clients_share as clients
|
||||||
from manila_tempest_tests import share_exceptions
|
from manila_tempest_tests import share_exceptions
|
||||||
|
from manila_tempest_tests import utils
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def rand_ip():
|
|
||||||
"""This uses the TEST-NET-3 range of reserved IP addresses.
|
|
||||||
|
|
||||||
Using this range, which are reserved solely for use in
|
|
||||||
documentation and example source code, should avoid any potential
|
|
||||||
conflicts in real-world testing.
|
|
||||||
"""
|
|
||||||
TEST_NET_3 = '203.0.113.'
|
|
||||||
final_octet = six.text_type(random.randint(0, 255))
|
|
||||||
return TEST_NET_3 + final_octet
|
|
||||||
|
|
||||||
|
|
||||||
class handle_cleanup_exceptions(object):
|
class handle_cleanup_exceptions(object):
|
||||||
"""Handle exceptions raised with cleanup operations.
|
"""Handle exceptions raised with cleanup operations.
|
||||||
|
|
||||||
@ -91,19 +78,7 @@ def network_synchronized(f):
|
|||||||
return wrapped_func
|
return wrapped_func
|
||||||
|
|
||||||
|
|
||||||
def is_microversion_supported(microversion):
|
skip_if_microversion_not_supported = utils.skip_if_microversion_not_supported
|
||||||
if (float(microversion) > float(CONF.share.max_api_microversion) or
|
|
||||||
float(microversion) < float(CONF.share.min_api_microversion)):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def skip_if_microversion_not_supported(microversion):
|
|
||||||
"""Decorator for tests that are microversion-specific."""
|
|
||||||
if not is_microversion_supported(microversion):
|
|
||||||
reason = ("Skipped. Test requires microversion '%s'." % microversion)
|
|
||||||
return testtools.skip(reason)
|
|
||||||
return lambda f: f
|
|
||||||
|
|
||||||
|
|
||||||
class BaseSharesTest(test.BaseTestCase):
|
class BaseSharesTest(test.BaseTestCase):
|
||||||
@ -125,7 +100,7 @@ class BaseSharesTest(test.BaseTestCase):
|
|||||||
method_isolated_creds = []
|
method_isolated_creds = []
|
||||||
|
|
||||||
def skip_if_microversion_not_supported(self, microversion):
|
def skip_if_microversion_not_supported(self, microversion):
|
||||||
if not is_microversion_supported(microversion):
|
if not utils.is_microversion_supported(microversion):
|
||||||
raise self.skipException(
|
raise self.skipException(
|
||||||
"Microversion '%s' is not supported." % microversion)
|
"Microversion '%s' is not supported." % microversion)
|
||||||
|
|
||||||
@ -657,8 +632,8 @@ class BaseSharesTest(test.BaseTestCase):
|
|||||||
data = {
|
data = {
|
||||||
"name": data_utils.rand_name("ss-name"),
|
"name": data_utils.rand_name("ss-name"),
|
||||||
"description": data_utils.rand_name("ss-desc"),
|
"description": data_utils.rand_name("ss-desc"),
|
||||||
"dns_ip": rand_ip(),
|
"dns_ip": utils.rand_ip(),
|
||||||
"server": rand_ip(),
|
"server": utils.rand_ip(),
|
||||||
"domain": data_utils.rand_name("ss-domain"),
|
"domain": data_utils.rand_name("ss-domain"),
|
||||||
"user": data_utils.rand_name("ss-user"),
|
"user": data_utils.rand_name("ss-user"),
|
||||||
"password": data_utils.rand_name("ss-password"),
|
"password": data_utils.rand_name("ss-password"),
|
||||||
|
93
manila_tempest_tests/utils.py
Normal file
93
manila_tempest_tests/utils.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# Copyright 2015 Mirantis Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
|
||||||
|
import six
|
||||||
|
from tempest import config
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
def get_microversion_as_tuple(microversion_str):
|
||||||
|
"""Transforms string-like microversion to two-value tuple of integers.
|
||||||
|
|
||||||
|
Tuple of integers useful for microversion comparisons.
|
||||||
|
"""
|
||||||
|
regex = r"^([1-9]\d*)\.([1-9]\d*|0)$"
|
||||||
|
match = re.match(regex, microversion_str)
|
||||||
|
if not match:
|
||||||
|
raise ValueError(
|
||||||
|
"Microversion does not fit template 'x.y' - %s" % microversion_str)
|
||||||
|
return int(match.group(1)), int(match.group(2))
|
||||||
|
|
||||||
|
|
||||||
|
def is_microversion_gt(left, right):
|
||||||
|
"""Is microversion for left is greater than the right one."""
|
||||||
|
return get_microversion_as_tuple(left) > get_microversion_as_tuple(right)
|
||||||
|
|
||||||
|
|
||||||
|
def is_microversion_ge(left, right):
|
||||||
|
"""Is microversion for left is greater than or equal to the right one."""
|
||||||
|
return get_microversion_as_tuple(left) >= get_microversion_as_tuple(right)
|
||||||
|
|
||||||
|
|
||||||
|
def is_microversion_eq(left, right):
|
||||||
|
"""Is microversion for left is equal to the right one."""
|
||||||
|
return get_microversion_as_tuple(left) == get_microversion_as_tuple(right)
|
||||||
|
|
||||||
|
|
||||||
|
def is_microversion_ne(left, right):
|
||||||
|
"""Is microversion for left is not equal to the right one."""
|
||||||
|
return get_microversion_as_tuple(left) != get_microversion_as_tuple(right)
|
||||||
|
|
||||||
|
|
||||||
|
def is_microversion_le(left, right):
|
||||||
|
"""Is microversion for left is less than or equal to the right one."""
|
||||||
|
return get_microversion_as_tuple(left) <= get_microversion_as_tuple(right)
|
||||||
|
|
||||||
|
|
||||||
|
def is_microversion_lt(left, right):
|
||||||
|
"""Is microversion for left is less than the right one."""
|
||||||
|
return get_microversion_as_tuple(left) < get_microversion_as_tuple(right)
|
||||||
|
|
||||||
|
|
||||||
|
def is_microversion_supported(microversion):
|
||||||
|
bottom = get_microversion_as_tuple(CONF.share.min_api_microversion)
|
||||||
|
microversion = get_microversion_as_tuple(microversion)
|
||||||
|
top = get_microversion_as_tuple(CONF.share.max_api_microversion)
|
||||||
|
return bottom <= microversion <= top
|
||||||
|
|
||||||
|
|
||||||
|
def skip_if_microversion_not_supported(microversion):
|
||||||
|
"""Decorator for tests that are microversion-specific."""
|
||||||
|
if not is_microversion_supported(microversion):
|
||||||
|
reason = ("Skipped. Test requires microversion '%s'." % microversion)
|
||||||
|
return testtools.skip(reason)
|
||||||
|
return lambda f: f
|
||||||
|
|
||||||
|
|
||||||
|
def rand_ip():
|
||||||
|
"""This uses the TEST-NET-3 range of reserved IP addresses.
|
||||||
|
|
||||||
|
Using this range, which are reserved solely for use in
|
||||||
|
documentation and example source code, should avoid any potential
|
||||||
|
conflicts in real-world testing.
|
||||||
|
"""
|
||||||
|
TEST_NET_3 = '203.0.113.'
|
||||||
|
final_octet = six.text_type(random.randint(0, 255))
|
||||||
|
return TEST_NET_3 + final_octet
|
Loading…
Reference in New Issue
Block a user