Allow to upload all data from nodes.csv file

There is possibility to upload CSV file, but only partial data
is fetched from it. Changed it to fetch everything what should
be included in form for adding new nodes.

Change-Id: Ic71d10c1911e034dd213212e74c1aa0d7a8bd252
This commit is contained in:
Dariusz Smigiel 2015-03-26 15:59:38 +01:00
parent 8615ccb701
commit bc3b4e08af
5 changed files with 157 additions and 33 deletions

View File

@ -8,6 +8,7 @@ Contents:
README
install
user_guide
HACKING
Indices and tables

16
doc/source/user_guide.rst Normal file
View 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.

View File

@ -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(

View File

@ -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):

View File

@ -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