summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Wagner <matt.wagner@redhat.com>2013-07-19 10:13:04 -0400
committerTomas Sedovic <tomas@sedovic.cz>2013-08-01 16:18:57 +0200
commit410f602735987c791c3a370d68372332c3e30e2f (patch)
treebe46618a66ee5ded0ca3b67c7248c0d324b41eb8
parent8cdd8b24337a2e4843a51d6968a4cf6dab822136 (diff)
downloadtuskar-ui-410f602735987c791c3a370d68372332c3e30e2f.tar.gz
Early, untested support for Node creation
This remains a work in progress. jprovazn update: this is and updated version of matt's patch (I had to squash them because gerrit would send both patches separately). With this update, nodes creation should work, uou have to do some setup to be able to talk to nova baremetal: 1) ssh tunnels (local ports must not override local openstack ports): ssh -NfL 5001:192.0.2.2:5000 demo@example.com ssh -NfL 9774:192.0.2.2:8774 demo@example.com 2) set NOVA_BAREMETAL_CREDS in openstack_dashboard/local/local_settings.py: these are creds for the nova baremetal openstack. bypass_url has admin's tenant id at the end, you can check/get tenant id by running "keystone --debug --os-username=admin --os-password=unset --os-tenant-name=admin --os-auth-url=http://192.0.2.2:5000/v2.0/ endpoint-list" on a test/demo server Change-Id: I0d5c4860fd5fca2926c8161d0c65d7b1e001ebb9
-rw-r--r--openstack_dashboard/api/nova.py2
-rw-r--r--openstack_dashboard/api/tuskar.py39
-rw-r--r--openstack_dashboard/dashboards/infrastructure/fixtures/initial_data.json6
-rw-r--r--openstack_dashboard/dashboards/infrastructure/models.py2
-rw-r--r--openstack_dashboard/dashboards/infrastructure/resource_management/nodes/tables.py4
-rw-r--r--openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py5
-rw-r--r--openstack_dashboard/dashboards/infrastructure/resource_management/racks/workflows.py81
-rw-r--r--openstack_dashboard/local/local_settings.py.example8
8 files changed, 107 insertions, 40 deletions
diff --git a/openstack_dashboard/api/nova.py b/openstack_dashboard/api/nova.py
index ba42c9b0..35535183 100644
--- a/openstack_dashboard/api/nova.py
+++ b/openstack_dashboard/api/nova.py
@@ -30,8 +30,10 @@ from django.utils.translation import ugettext_lazy as _
from novaclient.v1_1 import client as nova_client
from novaclient.v1_1.contrib.list_extensions import ListExtManager
from novaclient.v1_1 import security_group_rules as nova_rules
+from novaclient.v1_1.contrib import baremetal
from novaclient.v1_1.security_groups import SecurityGroup as NovaSecurityGroup
from novaclient.v1_1.servers import REBOOT_HARD
+from novaclient.v1_1.servers import REBOOT_SOFT
from horizon.conf import HORIZON_CONFIG
from horizon.utils.memoized import memoized
diff --git a/openstack_dashboard/api/tuskar.py b/openstack_dashboard/api/tuskar.py
index 2ebdeb45..46841017 100644
--- a/openstack_dashboard/api/tuskar.py
+++ b/openstack_dashboard/api/tuskar.py
@@ -27,12 +27,13 @@ from horizon import exceptions
from tuskarclient.v1 import client as tuskar_client
-from openstack_dashboard.api import base
+from openstack_dashboard.api import base, nova
import openstack_dashboard.dashboards.infrastructure.models as dummymodels
LOG = logging.getLogger(__name__)
TUSKAR_ENDPOINT_URL = getattr(settings, 'TUSKAR_ENDPOINT_URL')
+NOVA_BAREMETAL_CREDS = getattr(settings, 'NOVA_BAREMETAL_CREDS')
# FIXME: request isn't used right in the tuskar client right now, but looking
@@ -137,11 +138,26 @@ class Node(StringIdAPIResourceWrapper):
"""Wrapper for the Node object returned by the
dummy model.
"""
- _attrs = ['name', 'mac_address', 'ip_address', 'status', 'usage', 'rack']
+ _attrs = ['id', 'pm_address', 'cpus', 'memory_mb', 'service_host',
+ 'local_gb']
+
+ @classmethod
+ def manager(cls):
+ nc = nova.nova_client.Client(
+ NOVA_BAREMETAL_CREDS['user'],
+ NOVA_BAREMETAL_CREDS['password'],
+ NOVA_BAREMETAL_CREDS['tenant'],
+ auth_url=NOVA_BAREMETAL_CREDS['auth_url'],
+ bypass_url=NOVA_BAREMETAL_CREDS['bypass_url'])
+ return nova.baremetal.BareMetalNodeManager(nc)
@classmethod
def get(cls, request, node_id):
- return cls(dummymodels.Node.objects.get(id=node_id))
+ return cls(cls.manager().get(node_id))
+
+ @classmethod
+ def list(cls, request):
+ return cls.manager().list()
@classmethod
def list_unracked(cls, request):
@@ -292,9 +308,16 @@ class Node(StringIdAPIResourceWrapper):
self._alerts = [Alert(a) for a in
dummymodels.Alert.objects
.filter(object_type='node')
- .filter(object_id=int(self.id))]
+ .filter(object_id=str(self.id))]
return self._alerts
+ @property
+ def mac_address(self):
+ try:
+ return self._apiresource.interfaces[0]['address']
+ except:
+ return None
+
class Rack(StringIdAPIResourceWrapper):
"""Wrapper for the Rack object returned by the
@@ -364,10 +387,10 @@ class Rack(StringIdAPIResourceWrapper):
## fetch nodes from nova baremetal
@property
def list_nodes(self):
- return []
- #if not hasattr(self, '_nodes'):
- # self._nodes = [Node(h) for h in self._apiresource.node_set.all()]
- #return self._nodes
+ if not hasattr(self, '_nodes'):
+ self._nodes = [Node.get(None, node['id']) for node in (
+ self._apiresource.nodes)]
+ return self._nodes
def nodes_count(self):
return len(self._apiresource.nodes)
diff --git a/openstack_dashboard/dashboards/infrastructure/fixtures/initial_data.json b/openstack_dashboard/dashboards/infrastructure/fixtures/initial_data.json
index 5cbd8e2c..281af6e4 100644
--- a/openstack_dashboard/dashboards/infrastructure/fixtures/initial_data.json
+++ b/openstack_dashboard/dashboards/infrastructure/fixtures/initial_data.json
@@ -88,7 +88,7 @@
{"pk": 53, "model": "infrastructure.capacity", "fields": {"value": 160, "unit": "GB", "object_id": 7, "content_type": ["infrastructure", "flavortemplate"], "name": "storage"}},
{"pk": 54, "model": "infrastructure.capacity", "fields": {"value": 0, "unit": "GB", "object_id": 7, "content_type": ["infrastructure", "flavortemplate"], "name": "ephemeral_disk"}},
{"pk": 55, "model": "infrastructure.capacity", "fields": {"value": 0, "unit": "MB", "object_id": 7, "content_type": ["infrastructure", "flavortemplate"], "name": "swap_disk"}},
- {"pk": 1, "model": "infrastructure.alert", "fields": {"message": "Switch is not accessible.", "object_id": 1, "object_type": "rack", "time": "2011-09-01T13:20:30+03:00"}},
- {"pk": 2, "model": "infrastructure.alert", "fields": {"message": "Nova service is not running.", "object_id": 1, "object_type": "node", "time": "2011-09-01T13:20:30+03:00"}},
- {"pk": 3, "model": "infrastructure.alert", "fields": {"message": "Disk usage is over 90%.", "object_id": 1, "object_type": "node", "time": "2011-09-01T13:20:30+03:00"}}
+ {"pk": 1, "model": "infrastructure.alert", "fields": {"message": "Switch is not accessible.", "object_id": "1", "object_type": "rack", "time": "2011-09-01T13:20:30+03:00"}},
+ {"pk": 2, "model": "infrastructure.alert", "fields": {"message": "Nova service is not running.", "object_id": "1", "object_type": "node", "time": "2011-09-01T13:20:30+03:00"}},
+ {"pk": 3, "model": "infrastructure.alert", "fields": {"message": "Disk usage is over 90%.", "object_id": "1", "object_type": "node", "time": "2011-09-01T13:20:30+03:00"}}
]
diff --git a/openstack_dashboard/dashboards/infrastructure/models.py b/openstack_dashboard/dashboards/infrastructure/models.py
index 513e463d..8097e5b1 100644
--- a/openstack_dashboard/dashboards/infrastructure/models.py
+++ b/openstack_dashboard/dashboards/infrastructure/models.py
@@ -34,7 +34,7 @@ class Alert(models.Model):
class Meta:
db_table = 'infrastructure_alerts'
- object_id = models.PositiveIntegerField()
+ object_id = models.CharField(max_length=50)
object_type = models.CharField(max_length=20)
message = models.CharField(max_length=250)
time = models.DateTimeField()
diff --git a/openstack_dashboard/dashboards/infrastructure/resource_management/nodes/tables.py b/openstack_dashboard/dashboards/infrastructure/resource_management/nodes/tables.py
index 0b54922d..f1b19783 100644
--- a/openstack_dashboard/dashboards/infrastructure/resource_management/nodes/tables.py
+++ b/openstack_dashboard/dashboards/infrastructure/resource_management/nodes/tables.py
@@ -39,12 +39,12 @@ class NodesTable(tables.DataTable):
("active", True),
("error", False),
)
- name = tables.Column("name",
+ service_host = tables.Column("service_host",
link=("horizon:infrastructure:"
"resource_management:nodes:detail"),
verbose_name=_("Name"))
mac_address = tables.Column("mac_address", verbose_name=_("MAC Address"))
- ip_address = tables.Column("ip_address", verbose_name=_("IP Address"))
+ pm_address = tables.Column("pm_address", verbose_name=_("IP Address"))
status = tables.Column("status",
verbose_name=_("Status"),
status=True,
diff --git a/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py b/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py
index 2ab8bf82..b3bf1872 100644
--- a/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py
+++ b/openstack_dashboard/dashboards/infrastructure/resource_management/racks/views.py
@@ -67,11 +67,10 @@ class EditView(workflows.WorkflowView):
def get_initial(self):
obj = api.tuskar.Rack.get(self.request, self.kwargs['rack_id'])
- mac_str = "\n".join([x.mac_address for x in obj.list_nodes])
+ # mac_str = "\n".join([x.mac_address for x in obj.list_nodes])
return {'name': obj.name, 'resource_class_id': obj.resource_class_id,
'location': obj.location, 'subnet': obj.subnet,
- 'state': obj.state, 'node_macs': mac_str,
- 'rack_id': self.kwargs['rack_id']}
+ 'state': obj.state, 'rack_id': self.kwargs['rack_id']}
class DetailEditView(EditView):
diff --git a/openstack_dashboard/dashboards/infrastructure/resource_management/racks/workflows.py b/openstack_dashboard/dashboards/infrastructure/resource_management/racks/workflows.py
index d415ee0d..0499dc86 100644
--- a/openstack_dashboard/dashboards/infrastructure/resource_management/racks/workflows.py
+++ b/openstack_dashboard/dashboards/infrastructure/resource_management/racks/workflows.py
@@ -26,28 +26,52 @@ import re
class NodeCreateAction(workflows.Action):
- node_macs = forms.CharField(label=_("MAC Addresses"),
- widget=forms.Textarea(attrs={'rows': 12, 'cols': 20}),
- required=False)
+ # node_macs = forms.CharField(label=_("MAC Addresses"),
+ # widget=forms.Textarea(attrs={'rows': 12, 'cols': 20}),
+ # required=False)
+
+ node_name = forms.CharField(label="Name", required=True)
+ prov_mac_address = forms.CharField(label=("MAC Address"),
+ required=True)
+
+ # Hardware Specifications
+ cpus = forms.CharField(label="CPUs", required=True)
+ memory_mb = forms.CharField(label="Memory", required=True)
+ local_gb = forms.CharField(label="Local Disk (GB)", required=True)
+
+ # Power Management
+ pm_address = forms.CharField(label="IP Address", required=True)
+ pm_user = forms.CharField(label="User", required=True)
+ pm_password = forms.CharField(label="Password",
+ required=True,
+ widget=forms.PasswordInput(
+ render_value=False))
+
+ # Access
+ terminal_port = forms.CharField(label="Terminal Port", required=False)
class Meta:
name = _("Nodes")
-# mawagner FIXME - We presently disable this box, but leave the form.
-# We need to decide if this should be supported, or whether users should
-# be required to use the table view (to be implemented).
-# NOTE: The form input is disabled, but the input is also ignored
-# even if it were present.
-class NodeEditAction(workflows.Action):
- node_macs = forms.CharField(label=_("MAC Addresses"),
- widget=forms.Textarea(attrs={'rows': 12, 'cols': 20,
- 'readonly': 'readonly'}),
- required=False)
+# mawagner FIXME - For the demo, all we can really do is edit the one
+# associated node. That's very much _not_ what this form is actually
+# about, though.
+class NodeEditAction(NodeCreateAction):
class Meta:
name = _("Nodes")
+ # FIXME: mawagner - This is all for debugging. The idea is to fetch
+ # the first node and display it in the form; the latter part needs
+ # implementation. This also needs error handling; right now for testing
+ # I want to let it fail, but don't commit like that! :)
+ def __init__(self, request, *args, **kwargs):
+ super(NodeEditAction, self).__init__(request, *args, **kwargs)
+ rack_id = self.initial['rack_id']
+ rack = api.tuskar.Rack.get(request, rack_id)
+ nodes = rack.list_nodes
+
class RackCreateInfoAction(workflows.Action):
name = forms.RegexField(label=_("Name"),
@@ -121,17 +145,19 @@ class EditRackInfo(CreateRackInfo):
class CreateNodes(workflows.Step):
action_class = NodeCreateAction
- contributes = ('node_macs',)
+ contributes = ('node_name', 'prov_mac_address', 'cpus', 'memory_mb',
+ 'local_gb', 'pm_address', 'pm_user', 'pm_password',
+ 'terminal_port')
def get_nodes_data():
pass
-class EditNodes(workflows.Step):
+class EditNodes(CreateNodes):
action_class = NodeEditAction
depends_on = ('rack_id',)
contributes = ('node_macs',)
- help_text = _("Editing nodes via textbox is not presently supported.")
+ # help_text = _("Editing nodes via textbox is not presently supported.")
class CreateRack(workflows.Workflow):
@@ -144,17 +170,28 @@ class CreateRack(workflows.Workflow):
def handle(self, request, data):
try:
+ if data['node_name'] is not None:
+ node = api
+ node = api.tuskar.Node.manager().create(data['node_name'],
+ data['cpus'], data['memory_mb'],
+ data['local_gb'], data['prov_mac_address'],
+ data['pm_address'], data['pm_user'],
+ data['pm_password'], data['terminal_port'])
+
+ if node:
+ node_id = node.id
+ else:
+ node_id = None
+
+ # Then, register the Rack, including the node if it exists
rack = api.tuskar.Rack.create(request, data['name'],
data['resource_class_id'],
data['location'],
- data['subnet'])
-
- if data['node_macs'] is not None:
- nodes = data['node_macs'].splitlines(False)
- api.tuskar.Rack.register_nodes(rack, nodes)
+ data['subnet'],
+ [{'id': node_id}])
return True
- except:
+ except Exception as ex:
exceptions.handle(request, _("Unable to create rack."))
diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example
index f0a2f142..821304cd 100644
--- a/openstack_dashboard/local/local_settings.py.example
+++ b/openstack_dashboard/local/local_settings.py.example
@@ -355,4 +355,10 @@ SECURITY_GROUP_RULES = {
# FIXME: this will eventually be unneeded, as it will be retrieved from Keystone
#TUSKAR_ENDPOINT_URL = "http://127.0.0.1:6385"
-
+#NOVA_BAREMETAL_CREDS = {
+# 'user': 'admin',
+# 'password': 'admin_password_here',
+# 'tenant': 'admin',
+# 'auth_url': 'http://localhost:5001/v2.0/',
+# 'bypass_url': 'http://localhost:9774/v2/692567cd99f84f5d8f26ec23ff0ba460'
+#}