Merge "Allow to upload all data from nodes.csv file"
This commit is contained in:
commit
a35158bf42
@ -8,6 +8,7 @@ Contents:
|
||||
|
||||
README
|
||||
install
|
||||
user_guide
|
||||
HACKING
|
||||
|
||||
Indices and tables
|
||||
|
16
doc/source/user_guide.rst
Normal file
16
doc/source/user_guide.rst
Normal file
@ -0,0 +1,16 @@
|
||||
==========
|
||||
User Guide
|
||||
==========
|
||||
|
||||
Nodes List File
|
||||
---------------
|
||||
|
||||
To allow users to load a bunch of nodes at once, there is possibility to
|
||||
upload CSV file with given list of nodes. This file should be formatted as
|
||||
|
||||
::
|
||||
|
||||
driver,address,username,password/ssh key,mac addresses,cpu architecture,number of CPUs,available memory,available storage
|
||||
|
||||
Even if there is no all data available, we assume empty values for missing
|
||||
keys and try to parse everything, what is possible.
|
@ -11,8 +11,6 @@
|
||||
# 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 csv
|
||||
|
||||
import django.forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import exceptions
|
||||
@ -21,6 +19,7 @@ from horizon import messages
|
||||
|
||||
from tuskar_ui import api
|
||||
import tuskar_ui.forms
|
||||
from tuskar_ui.utils import utils
|
||||
|
||||
|
||||
DEFAULT_KERNEL_IMAGE_NAME = 'bm-deploy-kernel'
|
||||
@ -307,38 +306,13 @@ class UploadNodeForm(forms.SelfHandlingForm):
|
||||
return True
|
||||
|
||||
def get_data(self):
|
||||
data = []
|
||||
try:
|
||||
output = utils.parse_csv_file(self.cleaned_data['csv_file'])
|
||||
except ValueError as e:
|
||||
messages.error(self.request, e.message)
|
||||
output = []
|
||||
|
||||
for row in csv.reader(self.cleaned_data['csv_file']):
|
||||
try:
|
||||
driver = row[0].strip()
|
||||
except IndexError:
|
||||
messages.error(self.request,
|
||||
_("Unable to parse the CSV file."))
|
||||
return []
|
||||
|
||||
if driver == 'pxe_ssh':
|
||||
node = dict(
|
||||
ssh_address=row[1],
|
||||
ssh_username=row[2],
|
||||
ssh_key_contents=row[3],
|
||||
mac_addresses=row[4],
|
||||
driver=driver,
|
||||
)
|
||||
data.append(node)
|
||||
elif driver == 'pxe_ipmitool':
|
||||
node = dict(
|
||||
ipmi_address=row[1],
|
||||
ipmi_username=row[2],
|
||||
ipmi_password=row[3],
|
||||
driver=driver,
|
||||
)
|
||||
data.append(node)
|
||||
else:
|
||||
messages.error(self.request,
|
||||
_("Unknown driver: %s.") % driver)
|
||||
return []
|
||||
return data
|
||||
return output
|
||||
|
||||
|
||||
RegisterNodeFormset = django.forms.formsets.formset_factory(
|
||||
|
@ -15,6 +15,7 @@
|
||||
import collections
|
||||
import datetime
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import mock
|
||||
|
||||
from tuskar_ui.test import helpers
|
||||
@ -77,6 +78,92 @@ class UtilsTests(helpers.TestCase):
|
||||
ret = utils.safe_int_cast(object())
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_parse_correct_csv_file(self):
|
||||
correct_file = [
|
||||
'pxe_ipmitool,ipmi_address,ipmi_username,ipmi_password,'
|
||||
'mac_addresses,cpu_arch,cpus,memory_mb,local_gb',
|
||||
'pxe_ipmitool,,,,MAC_ADDRESS,,CPUS,,LOCAL_GB',
|
||||
'pxe_ssh,ssh_address,ssh_username,ssh_key_contents,mac_addresses'
|
||||
',cpu_arch,cpus,memory_mb,local_gb',
|
||||
'pxe_ssh,SSH,USER,KEY',
|
||||
'pxe_ssh,SSH,USER,,,CPU_ARCH',
|
||||
]
|
||||
|
||||
correct_data = utils.parse_csv_file(correct_file)
|
||||
|
||||
self.assertSequenceEqual(
|
||||
correct_data, [
|
||||
{
|
||||
'driver': 'pxe_ipmitool',
|
||||
'ipmi_address': 'ipmi_address',
|
||||
'ipmi_username': 'ipmi_username',
|
||||
'ipmi_password': 'ipmi_password',
|
||||
'mac_addresses': 'mac_addresses',
|
||||
'cpu_arch': 'cpu_arch',
|
||||
'cpus': 'cpus',
|
||||
'memory_mb': 'memory_mb',
|
||||
'local_gb': 'local_gb',
|
||||
}, {
|
||||
'driver': 'pxe_ipmitool',
|
||||
'ipmi_address': '',
|
||||
'ipmi_username': '',
|
||||
'ipmi_password': '',
|
||||
'mac_addresses': 'MAC_ADDRESS',
|
||||
'cpu_arch': '',
|
||||
'cpus': 'CPUS',
|
||||
'memory_mb': '',
|
||||
'local_gb': 'LOCAL_GB',
|
||||
}, {
|
||||
'driver': 'pxe_ssh',
|
||||
'ssh_address': 'ssh_address',
|
||||
'ssh_username': 'ssh_username',
|
||||
'ssh_key_contents': 'ssh_key_contents',
|
||||
'mac_addresses': 'mac_addresses',
|
||||
'cpu_arch': 'cpu_arch',
|
||||
'cpus': 'cpus',
|
||||
'memory_mb': 'memory_mb',
|
||||
'local_gb': 'local_gb',
|
||||
},
|
||||
{
|
||||
'driver': 'pxe_ssh',
|
||||
'ssh_address': 'SSH',
|
||||
'ssh_username': 'USER',
|
||||
'ssh_key_contents': 'KEY',
|
||||
},
|
||||
{
|
||||
'driver': 'pxe_ssh',
|
||||
'ssh_address': 'SSH',
|
||||
'ssh_username': 'USER',
|
||||
'ssh_key_contents': '',
|
||||
'mac_addresses': '',
|
||||
'cpu_arch': 'CPU_ARCH',
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
def test_parse_csv_file_wrong(self):
|
||||
no_csv_file = [
|
||||
'',
|
||||
'File with first empty line -- it\'s not a CSV file.',
|
||||
]
|
||||
|
||||
with self.assertRaises(ValueError) as raised:
|
||||
utils.parse_csv_file(no_csv_file)
|
||||
|
||||
self.assertEqual(unicode(raised.exception.message),
|
||||
unicode(_("Unable to parse the CSV file.")))
|
||||
|
||||
def test_parse_wrong_driver_file(self):
|
||||
wrong_driver_file = [
|
||||
'wrong_driver,ssh_address,ssh_user',
|
||||
]
|
||||
|
||||
with self.assertRaises(ValueError) as raised:
|
||||
utils.parse_csv_file(wrong_driver_file)
|
||||
|
||||
self.assertEqual(unicode(raised.exception.message),
|
||||
unicode(_("Unknown driver: %s.") % 'wrong_driver'))
|
||||
|
||||
|
||||
class MeteringTests(helpers.TestCase):
|
||||
def test_query_data(self):
|
||||
|
@ -11,8 +11,12 @@
|
||||
# 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 csv
|
||||
from itertools import izip
|
||||
import re
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
CAMEL_RE = re.compile(r'([A-Z][a-z]+|[A-Z]+(?=[A-Z\s]|$))')
|
||||
|
||||
|
||||
@ -92,3 +96,45 @@ def safe_int_cast(value):
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return 0
|
||||
|
||||
|
||||
def parse_csv_file(csv_file):
|
||||
"""Parses given CSV file.
|
||||
|
||||
If there is no error, it returns list of dicts. When something went wrong,
|
||||
list is empty, but warning contains appropriate information about
|
||||
possible problems.
|
||||
"""
|
||||
|
||||
parsed_data = []
|
||||
|
||||
for row in csv.reader(csv_file):
|
||||
try:
|
||||
driver = row[0].strip()
|
||||
except IndexError:
|
||||
raise ValueError(_("Unable to parse the CSV file."))
|
||||
|
||||
if driver in ('pxe_ssh', 'pxe_ipmitool'):
|
||||
node_keys = (
|
||||
'mac_addresses', 'cpu_arch', 'cpus', 'memory_mb', 'local_gb')
|
||||
|
||||
if driver == 'pxe_ssh':
|
||||
driver_keys = (
|
||||
'driver', 'ssh_address', 'ssh_username',
|
||||
'ssh_key_contents'
|
||||
)
|
||||
|
||||
elif driver == 'pxe_ipmitool':
|
||||
driver_keys = (
|
||||
'driver', 'ipmi_address', 'ipmi_username',
|
||||
'ipmi_password'
|
||||
)
|
||||
|
||||
node = dict(izip(driver_keys+node_keys, row))
|
||||
|
||||
parsed_data.append(node)
|
||||
|
||||
else:
|
||||
raise ValueError(_("Unknown driver: %s.") % driver)
|
||||
|
||||
return parsed_data
|
||||
|
Loading…
x
Reference in New Issue
Block a user