Merge "Changes to make AIO-DX setup work"

This commit is contained in:
Zuul 2023-07-21 13:08:28 +00:00 committed by Gerrit Code Review
commit f5407b51a7
14 changed files with 210 additions and 205 deletions

View File

@ -265,15 +265,6 @@ def parse_networking(parser: ArgumentParser):
choices=['hostonly', 'nat'],
type=str,
default='hostonly')
parser.add_argument("--nat-controller-floating-local-ssh-port", help=
"""
When oam network is configured as 'nat' a port on
the vbox host is used for connecting to ssh on
floating controller. No default value is
configured. This is mandatory if --vboxnet-type is
'nat' for non AIO-SX deployments.
""",
type=str)
parser.add_argument("--nat-controller0-local-ssh-port", help=
"""
When oam network is configured as 'nat' a port on

View File

@ -163,7 +163,7 @@ running it):
--iso-location "$HOME/Downloads/stx-8.iso" \
--labname StarlingX --install-mode graphical \
--config-files-dir ./config/labSetupFiles/ \
--ansible-controller-config ./config/ansibleFiles/localhost.yml \
--ansible-controller-config ./config/ansibleFiles/simplex_localhost.yml \
--kubernetes-config-files ./config/kubeFiles/ \
--vboxnet-type nat \
--vboxnet-name NatNetwork \

View File

@ -0,0 +1,15 @@
system_mode: duplex
dns_servers:
- 1.1.1.1
- 8.8.8.8
external_oam_subnet: 10.10.10.0/24
external_oam_gateway_address: 10.10.10.1
external_oam_floating_address: 10.10.10.3
external_oam_node_0_address: 10.10.10.4
external_oam_node_1_address: 10.10.10.5
admin_username: admin
admin_password: Li69nux*
ansible_become_pass: <system-password>

View File

@ -8,6 +8,9 @@ VERBOSE_LEVEL=0
##For now ceph_storage variable will be set to true but can be changed before executing the script
CEPH_STORAGE="true"
#Identify setup type
SETUP_TYPE=$(system show | grep 'system_mode' | awk '{print $4}')
OPENRC=/etc/platform/openrc
source ${OPENRC}
@ -53,11 +56,31 @@ function log_command {
## Set OAM interface
function configure_OAM_interface {
#Set OAM_IF variable
log_command "OAM_IF=enp0s3"
#Associate OAM_IF with Controller-0
log_command "system host-if-modify controller-0 $OAM_IF -c platform"
log_command "system interface-network-assign controller-0 $OAM_IF oam"
if [ "$SETUP_TYPE" == "simplex" ]; then
#Set OAM_IF variable
log_command "OAM_IF=enp0s3"
#Associate OAM_IF with Controller-0
log_command "system host-if-modify controller-0 $OAM_IF -c platform"
log_command "system interface-network-assign controller-0 $OAM_IF oam"
else
#Set Variables
log_command "OAM_IF=enp0s3 && MGMT_IF=enp0s8"
log_command "system host-if-modify controller-0 lo -c none"
local IFNET_UUIDS=$(system interface-network-list controller-0 | awk '{if ($6=="lo") print $4;}')
for UUID in $IFNET_UUIDS; do
log_command "system interface-network-remove ${UUID}"
done
#Associate variables with Controller-0
log_command "system host-if-modify controller-0 $OAM_IF -c platform"
log_command "system interface-network-assign controller-0 $OAM_IF oam"
log_command "system host-if-modify controller-0 $MGMT_IF -c platform"
log_command "system interface-network-assign controller-0 $MGMT_IF mgmt"
log_command "system interface-network-assign controller-0 $MGMT_IF cluster-host"
fi
return 0
}

View File

@ -0,0 +1,83 @@
#!/bin/bash
## This file makes the necessary configuration for the unlock of the Controller-1
DATE_FORMAT="%Y-%m-%d %T"
LOG_FILE=${LOG_FILE:-"${HOME}/lab_setup_2.log"}
VERBOSE_LEVEL=0
#Identify setup type
SETUP_TYPE=$(system show | grep 'system_mode' | awk '{print $4}')
OPENRC=/etc/platform/openrc
source ${OPENRC}
function info {
local MSG="$1"
echo ${MSG}
echo $(date +"${DATE_FORMAT}") ${MSG} >> ${LOG_FILE}
}
function log_command {
local CMD=$1
local MSG="[${OS_USERNAME}@${OS_PROJECT_NAME}]> RUNNING: ${CMD}"
set +e
if [ ${VERBOSE_LEVEL} -gt 0 ]; then
echo ${MSG}
fi
echo $(date +"${DATE_FORMAT}") ${MSG} >> ${LOG_FILE}
if [ ${VERBOSE_LEVEL} -gt 1 ]; then
eval ${CMD} 2>&1 | tee -a ${LOG_FILE}
RET=${PIPESTATUS[0]}
else
eval ${CMD} &>> ${LOG_FILE}
RET=$?
fi
if [ ${RET} -ne 0 ]; then
info "COMMAND FAILED (rc=${RET}): ${CMD}"
info "==========================="
info "Check \"${LOG_FILE}\" for more details, fix the issues and"
info "re-run the failed command manually."
exit 1
fi
set -e
return ${RET}
}
function configure_OAM_MGMT_interfaces {
#Set OAM_IF variable
log_command "OAM_IF=enp0s3"
#Associate OAM_IF with Controller-0
log_command "system host-if-modify controller-1 $OAM_IF -c platform"
log_command "system interface-network-assign controller-1 $OAM_IF oam"
log_command "system interface-network-assign controller-1 mgmt0 cluster-host"
}
##Configure ceph storage in controller-1
function configure_ceph_storage {
echo "Setting host-based Ceph storage backend solution"
local CEPH=$(system storage-backend-list | grep 'ceph')
if [ -z "$CEPH" ]; then
echo "Ceph storage not set in controller-0, skipping process in controller-1"
else
#Adding OSD on controller-1
log_command "system host-disk-list controller-1"
log_command "system host-disk-list controller-1 | awk '/\/dev\/sdb/{print \$2}' | xargs -i system host-stor-add controller-1 {}"
log_command "system host-stor-list controller-1"
fi
}
configure_OAM_MGMT_interfaces
configure_ceph_storage

View File

@ -17,7 +17,6 @@ user = getpass.getuser()
if platform in ("win32", "win64"):
LOGPATH = "C:\\Temp\\pybox_logs"
PORT = 10000
else:
homedir = os.environ["HOME"]
LOGPATH = f"{homedir}/vbox_installer_logs"
LOGPATH = f"{homedir}/vbox_installer_logs"

View File

@ -178,17 +178,9 @@ class OAM:
class Serial:
"""The `Serial` class contains configurations for the serial ports."""
if platform in ("win32", "win64"):
SERIAL = {
"uartbase": "0x3F8",
"uartport": "4",
"uartmode": "tcpserver",
"uartpath": "10000",
}
else:
SERIAL = {
"uartbase": "0x3F8",
"uartport": "4",
"uartmode": "server",
"uartpath": "/tmp/",
}
SERIAL = {
"uartbase": "0x3F8",
"uartport": "4",
"uartmode": "tcpserver",
"uartpath": 10000,
}

View File

@ -853,73 +853,25 @@ class AddUartTestCase(unittest.TestCase):
"""
def setUp(self):
self.hostname = "test-host"
self.vm_config = {
"uartbase": "0x3F8",
"uartport": "4",
"uartmode": "file",
"uartpath": "/path/to/uart/",
"prefix": "test-prefix"
"uartpath": "1",
}
@patch('vboxmanage.env')
@patch('vboxmanage.platform', new='win32')
def test_add_uart_windows(self, mock_env):
def test_add_uart_windows(self):
"""
Test _add_uart method for Windows platform
Test _add_uart method
"""
mock_env.PORT = 1
result = vboxmanage._add_uart(self.hostname, self.vm_config)
result = vboxmanage._add_uart(self.vm_config)
expected = [
'--uart1', '0x3F8', '4', '--uartmode1', 'file', '1'
]
self.assertCountEqual(result, expected)
@patch('vboxmanage.platform', new='linux')
def test_add_uart_linux_controller(self):
"""
Test _add_uart method for Linux platform with 'controller-0' in hostname
"""
self.hostname = "test-host-controller-0"
result = vboxmanage._add_uart(self.hostname, self.vm_config)
expected = [
'--uart1', '0x3F8', '4', '--uartmode1', 'file',
'/path/to/uart/test-prefix_test-host-controller-0_serial'
]
self.assertCountEqual(result, expected)
@patch('vboxmanage.platform', new='linux')
def test_add_uart_linux_no_controller(self):
"""
Test _add_uart method for Linux platform without 'controller-0' in hostname
"""
result = vboxmanage._add_uart(self.hostname, self.vm_config)
expected = [
'--uart1', '0x3F8', '4', '--uartmode1', 'file',
'/path/to/uart/test-prefix_test-host'
]
self.assertCountEqual(result, expected)
@patch('vboxmanage.platform', new='linux')
def test_add_uart_linux_no_prefix(self):
"""
Test _add_uart method for Linux platform without prefix in vm_config
"""
del self.vm_config["prefix"]
result = vboxmanage._add_uart(self.hostname, self.vm_config)
expected = [
'--uart1', '0x3F8', '4', '--uartmode1', 'file',
'/path/to/uart/test-host'
]
self.assertCountEqual(result, expected)
class ContainsValueTestCase(unittest.TestCase):
"""

View File

@ -14,7 +14,6 @@ import getpass
import time
from sys import platform
from consts import env
from utils.install_log import LOG
@ -388,7 +387,7 @@ def vboxmanage_modifyvm(hostname, vm_config=None):
cmd.extend([f'--nic{vm_config["nicnum"]}', "nat"])
if _is_uart_configured(vm_config):
uart_config = _add_uart(hostname, vm_config)
uart_config = _add_uart(vm_config)
cmd.extend(uart_config)
if _contains_value("nicbootprio2", vm_config):
@ -494,12 +493,11 @@ def _is_uart_configured(vm_config):
)
def _add_uart(hostname, vm_config):
def _add_uart(vm_config):
"""
Constructs a list of options for the UART device based on the values in vm_config.
Args:
hostname (str): Name of the virtual machine.
vm_config (dict): A dictionary representing the configuration options for the VM.
Returns:
@ -511,18 +509,7 @@ def _add_uart(hostname, vm_config):
uart_config.extend([f'{vm_config["uartport"]}'])
uart_config.extend(["--uartmode1"])
uart_config.extend([f'{vm_config["uartmode"]}'])
prefix = ""
if platform in ("win32", "win64"):
uart_config.extend([f"{env.PORT}"])
env.PORT += 1
else:
if _contains_value("prefix", vm_config):
prefix = f'{vm_config["prefix"]}_'
if "controller-0" in hostname:
uart_config.extend([f'{vm_config["uartpath"]}{prefix}{hostname}_serial'])
else:
uart_config.extend([f'{vm_config["uartpath"]}{prefix}{hostname}'])
uart_config.extend([f'{vm_config["uartpath"]}'])
return uart_config

View File

@ -16,7 +16,6 @@ import re
import tempfile
import signal
import sys
from sys import platform
import paramiko
import streamexpect
import ruamel.yaml
@ -336,7 +335,6 @@ def create_lab(m_vboxoptions):
nodes_list.append(node_name)
LOG.info("#### We will create the following nodes: %s", nodes_list)
port = 10000
# pylint: disable=too-many-nested-blocks
for node in nodes_list:
LOG.info("#### Creating node: %s", node)
@ -381,28 +379,16 @@ def create_lab(m_vboxoptions):
disk_sizes = item['disks'][no_disks]
vboxmanage.vboxmanage_createmedium(node, disk_sizes,
vbox_home_dir=m_vboxoptions.vbox_home_dir)
if platform in ("win32", "win64"):
vboxmanage.vboxmanage_modifyvm(
node,
{
"uartbase": serial_config[0]['uartbase'],
"uartport": serial_config[0]['uartport'],
"uartmode": serial_config[0]['uartmode'],
"uartpath": port,
},
)
port += 1
else:
vboxmanage.vboxmanage_modifyvm(
node,
{
"uartbase": serial_config[0]['uartbase'],
"uartport": serial_config[0]['uartport'],
"uartmode": serial_config[0]['uartmode'],
"uartpath": serial_config[0]['uartpath'],
"prefix": m_vboxoptions.userid,
},
)
vboxmanage.vboxmanage_modifyvm(
node,
{
"uartbase": serial_config[0]['uartbase'],
"uartport": serial_config[0]['uartport'],
"uartmode": serial_config[0]['uartmode'],
"uartpath": serial_config[0]['uartpath'],
},
)
serial_config[0]['uartpath'] += 1
if "controller" in node:
node_type = "controller"
@ -476,14 +462,6 @@ def create_lab(m_vboxoptions):
guest_ip=ip_addr
)
# Floating ip port forwarding
if m_vboxoptions.vboxnet_type == 'nat' and m_vboxoptions.setup_type != 'AIO-SX':
local_port = m_vboxoptions.nat_controller_floating_local_ssh_port
ip_addr = m_vboxoptions.controller_floating_ip
name = m_vboxoptions.labname + 'controller-float'
create_port_forward(name, m_vboxoptions.vboxnet_name,
local_port=local_port, guest_port='22', guest_ip=ip_addr)
ctrlr0 = m_vboxoptions.labname + '-controller-0'
vboxmanage.vboxmanage_storagectl(
ctrlr0,
@ -550,7 +528,7 @@ def get_hostnames(ignore=None, personalities=['controller', 'storage', 'worker']
node_name = V_BOX_OPTIONS.labname + f"-controller-{node_id}"
if ignore and node_name in ignore:
continue
hostnames[node_name] = f"controller-{id}"
hostnames[node_name] = f"controller-{node_id}"
if V_BOX_OPTIONS.workers and 'worker' in personalities:
for node_id in range(0, V_BOX_OPTIONS.workers):
node_name = V_BOX_OPTIONS.labname + f"-worker-{node_id}"
@ -666,7 +644,7 @@ def create_host_bulk_add():
def wait_for_hosts(ssh_client, hostnames, status,
timeout=HostTimeout.HOST_INSTALL, interval=20):
timeout=HostTimeout.HOST_INSTALL, interval=30):
"""
Wait for a given interval for the host(s) to reach the expected
status.
@ -835,9 +813,10 @@ def connect_to_serial(func):
return func_wrapper
def _connect_to_ssh():
def _connect_to_ssh(my_stage=1):
# Get ip and port for ssh on floating ip
ip_addr, port = get_ssh_ip_and_port()
ip_addr, port = get_ssh_ip_and_port(my_stage=my_stage)
# Remove ssh key
# For hostonly adapter we remove port 22 of controller ip
@ -879,7 +858,11 @@ def connect_to_ssh(func):
def func_wrapper(*args, **kwargs):
try:
ssh = _connect_to_ssh()
if 'm_stage' in kwargs and 'm_stage' is not None:
m_stage = kwargs['m_stage']
ssh = _connect_to_ssh(m_stage)
else:
ssh = _connect_to_ssh()
kwargs['ssh_client'] = ssh
return func(*args, **kwargs)
finally:
@ -978,8 +961,7 @@ def stage_config_controller(stream): # pylint: disable=too-many-locals
installed and that its serial console stream is open.
"""
ip_addr, port = get_ssh_ip_and_port(
'controller-0') # Floating ip is not yet configured
ip_addr, port = get_ssh_ip_and_port('controller-0') # Floating ip is not yet configured
#Update localhost.yml with system password
new_config_ansible = override_ansible_become_pass()
@ -1010,7 +992,7 @@ def stage_config_controller(stream): # pylint: disable=too-many-locals
install_lab.update_platform_cpus(stream, 'controller-0')
def get_ssh_ip_and_port(node='floating'):
def get_ssh_ip_and_port(node="", my_stage=1):
"""
This function returns the IP address and port of the specified node to use for
an SSH connection.
@ -1018,6 +1000,8 @@ def get_ssh_ip_and_port(node='floating'):
Args:
node (str, optional): The node to get the IP address and port for.
Valid values are "floating" (default), "controller-0", and "controller-1".
my_stage (int, optional): The stage of lab_setup. When an installation is
already started, use this value to determine which node to connect.
Returns:
tuple: A tuple containing the IP address and port of the specified node.
@ -1026,17 +1010,17 @@ def get_ssh_ip_and_port(node='floating'):
Exception: If an undefined node is specified.
"""
if my_stage in (1, 2):
node = 'controller-0'
else:
node = 'controller-1'
if V_BOX_OPTIONS.vboxnet_type == 'nat':
ip_addr = '127.0.0.1'
if node == 'floating':
if V_BOX_OPTIONS.setup_type != 'AIO-SX':
port = V_BOX_OPTIONS.nat_controller_floating_local_ssh_port
else:
port = V_BOX_OPTIONS.nat_controller0_local_ssh_port
elif node == 'controller-0':
if node == 'controller-0':
port = V_BOX_OPTIONS.nat_controller0_local_ssh_port
elif node == 'controller-1':
port = V_BOX_OPTIONS.nat_controller_1_local_ssh_port
port = V_BOX_OPTIONS.nat_controller1_local_ssh_port
else:
raise Exception(f"Undefined node '{node}'") # pylint: disable=E0012, W0719
else:
@ -1083,7 +1067,7 @@ def stage_rsync_config():
return
# Get ip and port for ssh on floating ip
ip_addr, port = get_ssh_ip_and_port()
ip_addr, port = get_ssh_ip_and_port('controller-0')
# Copy config files to controller
if V_BOX_OPTIONS.config_files_dir:
local_path = V_BOX_OPTIONS.config_files_dir
@ -1152,13 +1136,12 @@ def _run_lab_setup(m_stage, ssh_client):
def stage_lab_setup1():
"""Calls _run_lab_setup with ssh_client 1"""
_run_lab_setup(1) # pylint: disable=no-value-for-parameter
_run_lab_setup(m_stage=1) # pylint: disable=no-value-for-parameter
def stage_lab_setup2():
"""Calls _run_lab_setup with ssh_client 2"""
_run_lab_setup(2) # pylint: disable=no-value-for-parameter
_run_lab_setup(m_stage=2) # pylint: disable=no-value-for-parameter
def stage_lab_setup3():
@ -1300,8 +1283,8 @@ def stage_install_nodes(ssh_client):
for virtual_machine in powered_off:
LOG.info("#### Powering on VM: %s", virtual_machine)
vboxmanage.vboxmanage_startvm(virtual_machine, V_BOX_OPTIONS.headless, force=True)
LOG.info("Give VM 20s to boot.")
time.sleep(20)
LOG.info("Give VM 180s to boot.")
time.sleep(180)
ctrl0 = V_BOX_OPTIONS.labname + "-controller-0"
hostnames = list(get_hostnames(ignore=[ctrl0]).values())
@ -1334,6 +1317,7 @@ def stage_unlock_controller1(ssh_client):
timeout=60)
LOG.info("#### waiting for controller-1 to be available.")
time.sleep(120)
wait_for_hosts(ssh_client, ['controller-1'], 'available')
@ -1805,7 +1789,6 @@ AIO_DX_STAGES = [
STG_INSTALL_NODES,
STG_LAB_SETUP2,
STG_UNLOCK_CONTROLLER1,
STG_LAB_SETUP3,
STG_ENABLE_KUBERNETES,
]
@ -1927,10 +1910,6 @@ def validate(v_box_opt, m_stages):
err = False
# Generic
if v_box_opt.vboxnet_type == 'nat':
if v_box_opt.setup_type != AIO_SX:
if not v_box_opt.nat_controller_floating_local_ssh_port:
print("Please set --nat-controller-floating-local-ssh-port")
err = True
if not v_box_opt.nat_controller0_local_ssh_port:
print("Please set --nat-controller0-local-ssh-port")
err = True

View File

@ -451,16 +451,19 @@ class TestGetHostnames(unittest.TestCase):
mock_options.labname = "test"
expected = {
'test-controller-0': f'controller-{id}',
'test-controller-1': f'controller-{id}',
'test-controller-0': 'controller-0',
'test-controller-1': 'controller-1',
'test-worker-0': f'worker-{id}',
'test-worker-1': f'worker-{id}',
'test-storage-0': 'storage-0',
'test-storage-1': 'storage-1',
}
# Run and Assert
self.assertEqual(install_vbox.get_hostnames(), expected)
# Run
result = install_vbox.get_hostnames()
# Assert
self.assertEqual(result, expected)
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
def test_get_hostnames_with_ignore(self, mock_options):
@ -476,14 +479,17 @@ class TestGetHostnames(unittest.TestCase):
ignore = ['test-controller-0', 'test-worker-1']
expected = {
'test-controller-1': f'controller-{id}',
'test-controller-1': 'controller-1',
'test-worker-0': f'worker-{id}',
'test-storage-0': 'storage-0',
'test-storage-1': 'storage-1',
}
# Run and Assert
self.assertEqual(install_vbox.get_hostnames(ignore=ignore), expected)
# Run
result = install_vbox.get_hostnames(ignore=ignore)
# Assert
self.assertEqual(result, expected)
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
def test_get_hostnames_with_selected_personalities(self, mock_options):
@ -499,14 +505,17 @@ class TestGetHostnames(unittest.TestCase):
personalities = ['controller', 'worker']
expected = {
'test-controller-0': f'controller-{id}',
'test-controller-1': f'controller-{id}',
'test-controller-0': 'controller-0',
'test-controller-1': 'controller-1',
'test-worker-0': f'worker-{id}',
'test-worker-1': f'worker-{id}',
}
# Run and Assert
self.assertEqual(install_vbox.get_hostnames(personalities=personalities), expected)
# Run
result = install_vbox.get_hostnames(personalities=personalities)
# Assert
self.assertEqual(result, expected)
class TestGetPersonalities(unittest.TestCase):

View File

@ -10,7 +10,7 @@ using local domain socket.
import re
import socket
from sys import platform, stdout
from sys import stdout
import time
import streamexpect
from utils.install_log import LOG
@ -31,17 +31,13 @@ def connect(hostname, port=10000, prefix=""):
if 'controller-0' in hostname:
socketname += '_serial'
LOG.info("Connecting to %s at %s", hostname, socketname)
if platform in ('win32', 'win64'):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
else:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
try:
if platform in ('win32', 'win64'):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.connect(('localhost', port))
else:
sock.connect(socketname)
except: # pylint: disable=bare-except
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.connect(('localhost', port))
except: # pylint: disable=bare-except
LOG.info("Connection failed")
pass # pylint: disable=unnecessary-pass
# disconnect(sock)

View File

@ -9,35 +9,14 @@ class ConnectTestCase(unittest.TestCase):
Class to test connect method
"""
@patch("serial.LOG.info")
@patch("socket.socket")
def test_connect_unix(self, mock_socket, mock_log_info):
"""
Test connect method for Unix platform
"""
# Setup
serial.platform = 'linux'
mock_socket.return_value = mock_socket
hostname = 'hostname'
# Run
result = serial.connect(hostname)
# Assert
mock_socket.assert_called_once_with(socket.AF_UNIX, socket.SOCK_STREAM)
mock_socket.connect.assert_called_once_with(f"/tmp/{hostname}")
self.assertEqual(result, mock_socket)
@patch("serial.LOG.info")
@patch("socket.socket")
def test_connect_windows(self, mock_socket, mock_log_info):
"""
Test connect method for Windows platform
Test connect method
"""
# Setup
serial.platform = 'win32'
mock_socket.return_value = mock_socket
hostname = 'hostname'
port = 10000
@ -58,17 +37,17 @@ class ConnectTestCase(unittest.TestCase):
"""
# Setup
serial.platform = 'linux'
mock_socket.return_value = mock_socket
hostname = 'hostname'
port = 10000
mock_socket.connect.side_effect = Exception
# Run
result = serial.connect(hostname)
result = serial.connect(hostname, port)
# Assert
mock_socket.assert_called_once_with(socket.AF_UNIX, socket.SOCK_STREAM)
mock_socket.connect.assert_called_once_with(f"/tmp/{hostname}")
mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
mock_socket.connect.assert_called_once_with(('localhost', port))
mock_log_info.assert_called_with("Connection failed")
self.assertIsNone(result)