diff options
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' +#} |