├── vsphere ├── __init__.py ├── templatetags │ ├── __init__.py │ ├── multiply.py │ ├── percent.py │ └── divide.py ├── templates │ ├── search.html │ ├── resourcepool_list.html │ ├── guests.html │ ├── hypervisors.html │ ├── resourcepools.html │ ├── guest_list.html │ ├── hypervisor_list.html │ ├── home.html │ ├── guest.html │ ├── resourcepool.html │ ├── head.html │ ├── top10.html │ ├── datacenter.html │ ├── stats_by_resourcepool.html │ ├── hypervisor.html │ └── physical.html ├── urls.py ├── api.py ├── admin.py ├── models.py └── views.py ├── MANIFEST.in ├── screenshots ├── 4_guest_details.png ├── 1_home_datacenter.png ├── 2_hypervisors_list.png ├── 5_statistics_top10.png ├── 6_statistics_pools.png └── 3_hypervisor_details.png ├── tools ├── hypervisor.ini └── import_hypervisor.py ├── setup.py └── README.rst /vsphere/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vsphere/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | recursive-include vsphere/templates * 3 | recursive-include vsphere/templatetags * -------------------------------------------------------------------------------- /screenshots/4_guest_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julcollas/django-vsphere/HEAD/screenshots/4_guest_details.png -------------------------------------------------------------------------------- /tools/hypervisor.ini: -------------------------------------------------------------------------------- 1 | [user1] 2 | user:user1 3 | password:password1 4 | 5 | [user2] 6 | user:user2 7 | password:password2 -------------------------------------------------------------------------------- /screenshots/1_home_datacenter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julcollas/django-vsphere/HEAD/screenshots/1_home_datacenter.png -------------------------------------------------------------------------------- /screenshots/2_hypervisors_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julcollas/django-vsphere/HEAD/screenshots/2_hypervisors_list.png -------------------------------------------------------------------------------- /screenshots/5_statistics_top10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julcollas/django-vsphere/HEAD/screenshots/5_statistics_top10.png -------------------------------------------------------------------------------- /screenshots/6_statistics_pools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julcollas/django-vsphere/HEAD/screenshots/6_statistics_pools.png -------------------------------------------------------------------------------- /screenshots/3_hypervisor_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julcollas/django-vsphere/HEAD/screenshots/3_hypervisor_details.png -------------------------------------------------------------------------------- /vsphere/templatetags/multiply.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.filter 7 | def multiply(value, arg): 8 | return value * arg -------------------------------------------------------------------------------- /vsphere/templatetags/percent.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.filter 7 | def percent(value, arg): 8 | return int(100 * value / arg) -------------------------------------------------------------------------------- /vsphere/templatetags/divide.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.filter 7 | def divide(value, arg): 8 | return "%.2f" % (value / float(arg)) -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | import os 4 | 5 | README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() 6 | 7 | # allow setup.py to be run from any path 8 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 9 | 10 | setup( 11 | name = 'django-vsphere', 12 | version = '0.7', 13 | packages = ['vsphere'], 14 | include_package_data = True, 15 | description = 'Django app for vSphere Dashboard.', 16 | long_description = README, 17 | author = 'Julien Collas', 18 | author_email = 'jul.collas@gmail.com', 19 | install_requires=['django-tastypie', 'python-mimeparse'], 20 | classifiers = [ 21 | 'Environment :: Web Environment', 22 | 'Framework :: Django', 23 | 'Topic :: Software Development :: Libraries :: Python Modules', 24 | 'Programming Language :: Python', 25 | 'Topic :: Internet :: WWW/HTTP' 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /vsphere/templates/search.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load multiply %} 3 | {% load percent %} 4 | {% block title %} 5 | vSphere - Search 6 | {% endblock %} 7 | {% block content %} 8 | {% if resourcepool_list %} 9 |
10 |
{{ resourcepool_list |length }} Resource Pools
11 | {% include "resourcepool_list.html" %} 12 |
13 | {% endif %} 14 | 15 | {% if hypervisor_list %} 16 |
17 |
{{ hypervisor_list |length }} Hypervisors
18 | {% include "hypervisor_list.html" %} 19 |
20 | {% endif %} 21 | 22 | {% if guest_list %} 23 |
24 |
{{ guest_list |length }} Guests
25 | {% include "guest_list.html" %} 26 |
27 | {% endif %} 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /vsphere/urls.py: -------------------------------------------------------------------------------- 1 | from vsphere.api import GuestResource, HypervisorResource 2 | from django.conf.urls import patterns, include, url 3 | from tastypie.api import Api 4 | 5 | v1_api = Api(api_name='v1') 6 | v1_api.register(GuestResource()) 7 | v1_api.register(HypervisorResource()) 8 | 9 | urlpatterns = patterns( 10 | '', 11 | url(r'^$', 'vsphere.views.home'), 12 | url(r'^hypervisors$', 'vsphere.views.hypervisors'), 13 | url(r'^guests$', 'vsphere.views.guests'), 14 | url(r'^resourcepools$', 'vsphere.views.resourcepools'), 15 | url(r'^resourcepoolstats$', 'vsphere.views.stats_by_resourcepool'), 16 | url(r'^top10resourcepools$', 'vsphere.views.top10'), 17 | url(r'^physical$', 'vsphere.views.physical_statistics'), 18 | url(r'^datacenter$', 'vsphere.views.datacenter_statistics'), 19 | url(r'^hypervisor', 'vsphere.views.hypervisor_info'), 20 | url(r'^guest', 'vsphere.views.guest_info'), 21 | url(r'^resourcepool', 'vsphere.views.resourcepool'), 22 | url(r'^search', 'vsphere.views.search'), 23 | url(r'^api/', include(v1_api.urls)), 24 | ) 25 | -------------------------------------------------------------------------------- /vsphere/api.py: -------------------------------------------------------------------------------- 1 | from tastypie.resources import ModelResource 2 | from vsphere.models import Guest, Hypervisor 3 | from tastypie import fields 4 | 5 | 6 | class GuestResource(ModelResource): 7 | hypervisor = fields.CharField(attribute="hypervisor") 8 | 9 | class Meta: 10 | queryset = Guest.objects.all() 11 | include_resource_uri = False 12 | resource_name = 'guest' 13 | ordering = ['name'] 14 | limit = 0 15 | filtering = { 16 | 'name': ('exact', 'startswith', 'icontains'), 17 | } 18 | 19 | def determine_format(self, request): 20 | return 'application/json' 21 | 22 | 23 | class HypervisorResource(ModelResource): 24 | 25 | class Meta: 26 | queryset = Hypervisor.objects.all() 27 | include_resource_uri = False 28 | resource_name = 'hypervisor' 29 | ordering = ['name'] 30 | limit = 0 31 | filtering = { 32 | 'name': ('exact', 'startswith', 'icontains'), 33 | } 34 | 35 | def determine_format(self, request): 36 | return 'application/json' 37 | -------------------------------------------------------------------------------- /vsphere/templates/resourcepool_list.html: -------------------------------------------------------------------------------- 1 | {% load multiply %} 2 | {% load percent %} 3 | 4 | {% block content %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% regroup resourcepool_list|dictsort:"name" by name as resourcepool_list_name %} 17 | {% for a in resourcepool_list_name %} 18 | {% for rp in a.list %} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% endfor %} 27 | {% endfor %} 28 | 29 |
NameHosted GuestAverage MemoryAverage StorageAverage vCPU
{{ rp.name }}{{ rp.guests | length }}{{ rp.memory_avg | multiply:1048576 | filesizeformat }}{{ rp.storage_avg | multiply:1048576 | filesizeformat }}{{ rp.vcpu_avg | floatformat }}
30 | {% endblock %} -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | django-vsphere is an app for `Django `_ to have a centralized dashboard for all your VMWare vSphere ESXi. 2 | 3 | Dependencies 4 | ----------- 5 | 6 | - django-tastypie 7 | - python-mimeparse (un-resolved tastypie dependencie) 8 | 9 | Quick start 10 | ----------- 11 | 12 | 1. Add "vsphere" to your INSTALLED_APPS setting like this:: 13 | 14 | INSTALLED_APPS = ( 15 | ... 16 | 'vsphere', 17 | ) 18 | 19 | 2. Include the polls URLconf in your project urls.py like this:: 20 | 21 | url(r'^', include('vsphere.urls')), 22 | 23 | 4. Run syncdb to create the vsphere models:: 24 | 25 | python manage.py syncdb 26 | 27 | 5. Start the development server and visit http://127.0.0.1:8000/admin/ :: 28 | 29 | python manage.py runserver 30 | 31 | 6. Visit http://127.0.0.1:8000/ to display the dashboard. 32 | 33 | 7. Visit the guest API at http://127.0.0.1:8000/api/guest/?name=vm1 34 | 35 | 36 | Import 37 | ----------- 38 | 39 | 1. You can find an example script to import an hypervisor in "tools". 40 | 41 | 2. run import 42 | 43 | $ pip install pysphere 44 | 45 | $ python import_hypervisor.py -H hv.example.com -u user1 -d DatacenterX -p mypassword 46 | 47 | 3. The import script can use password stored in ~/.hypervisor.ini 48 | -------------------------------------------------------------------------------- /vsphere/templates/guests.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% block title %} 3 | vSphere - Guests 4 | {% endblock %} 5 | {% block content %} 6 |
7 |
{{ guest_list | length }} Guests
8 | {% include "guest_list.html" %} 9 |
10 | {% if guest_list.has_other_pages %} 11 | 34 | {% endif %} 35 | {% endblock %} -------------------------------------------------------------------------------- /vsphere/templates/hypervisors.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load i18n %} 3 | {% block title %} 4 | vSphere - Hypervisors 5 | {% endblock %} 6 | {% block content %} 7 |
8 |
{{ hypervisor_list | length }} Hypervisors
9 | {% include "hypervisor_list.html" %} 10 |
11 | 12 | {% if hypervisor_list.has_other_pages %} 13 | 36 | {% endif %} 37 | {% endblock %} -------------------------------------------------------------------------------- /vsphere/templates/resourcepools.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load i18n %} 3 | {% block title %} 4 | vSphere - Resource Pools 5 | {% endblock %} 6 | {% block content %} 7 |
8 |
{{ resourcepool_list | length }} Resource Pools
9 | {% include "resourcepool_list.html" %} 10 |
11 | 12 | {% if resourcepool_list.has_other_pages %} 13 | 36 | {% endif %} 37 | {% endblock %} -------------------------------------------------------------------------------- /vsphere/templates/guest_list.html: -------------------------------------------------------------------------------- 1 | {% load multiply %} 2 | {% load percent %} 3 | 4 | {% block content %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% for guest in guest_list %} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 36 | 37 | 38 | {% endfor %} 39 |
HostnameResource PoolMemoryDiskvCPUStatusHypervisor
{{ guest.name }}{{ guest.resourcePool }}{{ guest.memory | multiply:1048576 | filesizeformat }}{{ guest.get_disk_reserved | multiply:1048576 | filesizeformat }}{{ guest.vcpu }} 26 | {% if guest.poweredOn %} 27 | 28 | 29 | 30 | {% else %} 31 | 32 | 33 | 34 | {% endif %} 35 | {{ guest.get_hypervisor_name }}
40 | {% endblock %} -------------------------------------------------------------------------------- /vsphere/templates/hypervisor_list.html: -------------------------------------------------------------------------------- 1 | {% load multiply %} 2 | {% load percent %} 3 | 4 | {% block content %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% for hv in hypervisor_list %} 19 | 20 | 21 | 22 | 31 | 41 | 42 | 43 | 44 | 45 | {% endfor %} 46 | 47 |
HostnameHosted GuestReserved MemoryAllocated DiskCPU ThreadsAllocated vCpuDatacenter
{{ hv.name }}{{ hv.get_number_guest }} 23 |
24 |
27 | {{ hv.memoryReserved|percent:hv.memorySize }}% 28 |
29 |
30 |
32 |
33 |
37 | {{ hv.diskReserved|percent:hv.get_datastores_size }}% 38 |
39 |
40 |
{{ hv.numCpuThreads }}{{ hv.vcpuReserved }}{{ hv.datacenter }}
48 | {% endblock %} -------------------------------------------------------------------------------- /vsphere/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load multiply %} 3 | {% load percent %} 4 | {% block title %} 5 | vSphere - Dashboard 6 | {% endblock %} 7 | {% block content %} 8 |
9 | {% for dc in datacenter|dictsort:"name" %} 10 |
11 |
12 |

{{ dc.name }}

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 37 | 38 | 39 | 40 | 49 | 50 | {% if dc.raw_storage_reserved %} 51 | 52 | 53 | 54 | 55 | {% endif %} 56 |
Hypervisors{{ dc.nb_hv }}
Guests{{ dc.nb_guest }}
CPU Consolidation{{ dc.cpu_consolidation|floatformat:3 }}
Used Memory 29 |
30 |
33 | {{ dc.used_mem }} % 34 |
35 |
36 |
Used Storage 41 |
42 |
45 | {{ dc.used_storage }} % 46 |
47 |
48 |
Raw Storage{{ dc.raw_storage_reserved | multiply:1048576 | filesizeformat }}
57 |
58 |
59 | {% endfor %} 60 |
61 | {% endblock %} 62 | -------------------------------------------------------------------------------- /vsphere/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.contrib import admin 3 | from vsphere.models import Datastore 4 | from vsphere.models import Hypervisor 5 | from vsphere.models import Disk 6 | from vsphere.models import Guest 7 | from vsphere.models import Network 8 | from vsphere.models import Interface 9 | from vsphere.models import VirtualNic 10 | from vsphere.models import Vswitch 11 | 12 | 13 | class DatastoreAdmin(admin.ModelAdmin): 14 | """ 15 | DatastoreAdmin Class with display, filter and search settings 16 | """ 17 | list_display = ['hypervisor', 'name', 'capacity'] 18 | list_filter = ['hypervisor'] 19 | search_fields = ['hypervisor'] 20 | save_as = True 21 | 22 | 23 | class DiskAdmin(admin.ModelAdmin): 24 | """ 25 | DiskAdmin Class with display, filter and search settings 26 | """ 27 | list_display = ['guest', 'name', 'size', 'datastore'] 28 | list_filter = ['guest'] 29 | search_fields = ['guest'] 30 | save_as = True 31 | 32 | 33 | class GuestAdmin(admin.ModelAdmin): 34 | """ 35 | GuestAdmin Class with display, filter and search settings 36 | """ 37 | list_display = ['name', 'hypervisor', 'vcpu', 'memory', 'resourcePool'] 38 | list_filter = ['hypervisor', 'poweredOn', 'vcpu', 'memory', 'resourcePool'] 39 | search_fields = ['name', 'hypervisor', 'resourcePool'] 40 | save_as = True 41 | 42 | 43 | class HypervisorAdmin(admin.ModelAdmin): 44 | """ 45 | HypervisorAdmin Class with display, filter and search settings 46 | """ 47 | list_display = ['name', 'datacenter', 'vendor', 'memorySize'] 48 | list_filter = ['datacenter', 'vendor', 'memorySize'] 49 | search_fields = ['name'] 50 | save_as = True 51 | 52 | 53 | class InterfaceAdmin(admin.ModelAdmin): 54 | """ 55 | InterfaceAdmin Class with display, filter and search settings 56 | """ 57 | list_display = ['hypervisor', 'name', 'vswitch'] 58 | list_filter = ['hypervisor'] 59 | search_fields = ['hypervisor'] 60 | save_as = True 61 | 62 | 63 | class VirtualNicAdmin(admin.ModelAdmin): 64 | """ 65 | VirtualNicAdmin Class with display, filter and search settings 66 | """ 67 | list_display = ['guest', 'name', 'mac', 'network'] 68 | list_filter = ['guest'] 69 | search_fields = ['guest', 'name', 'network'] 70 | save_as = True 71 | 72 | 73 | class VswitchAdmin(admin.ModelAdmin): 74 | """ 75 | VirtualNicAdmin Class with display, filter and search settings 76 | """ 77 | list_display = ['hypervisor', 'name'] 78 | save_as = True 79 | 80 | 81 | class NetworkAdmin(admin.ModelAdmin): 82 | """ 83 | VirtualNicAdmin Class with display, filter and search settings 84 | """ 85 | list_display = ['name', 'vlanId'] 86 | save_as = True 87 | 88 | 89 | admin.site.register(Datastore, DatastoreAdmin) 90 | admin.site.register(Hypervisor, HypervisorAdmin) 91 | admin.site.register(Disk, DiskAdmin) 92 | admin.site.register(Guest, GuestAdmin) 93 | admin.site.register(Interface, InterfaceAdmin) 94 | admin.site.register(VirtualNic, VirtualNicAdmin) 95 | admin.site.register(Vswitch, VswitchAdmin) 96 | admin.site.register(Network, NetworkAdmin) 97 | -------------------------------------------------------------------------------- /vsphere/templates/guest.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load multiply %} 3 | {% load percent %} 4 | {% block title %} 5 | vSphere - {{ guest.name }} 6 | {% endblock %} 7 | {% block content %} 8 |

{{ guest.name }}

9 |
10 |
11 |
12 |
General
13 |
14 |
15 |
Guest OS
16 |
{{ guest.osVersion }}
17 |
18 |
19 |
vCpu
20 |
{{ guest.vcpu }}
21 |
22 |
23 |
Memory
24 |
{{ guest.memory | multiply:1048576 | filesizeformat }}
25 |
26 |
27 |
Status
28 |
29 | {% if guest.poweredOn %} 30 | 31 | 32 | 33 | {% else %} 34 | 35 | 36 | 37 | {% endif %} 38 |
39 |
40 |
41 |
Resource Pool
42 |
{{ guest.resourcePool }}
43 |
44 |
45 |
Datacenter
46 |
{{ guest.hypervisor.datacenter }}
47 |
48 |
49 |
Hosted on
50 | 51 |
52 |
53 |
Notes
54 |
{{ guest.annotation }}
55 |
56 |
57 |
Interfaces
58 |
{{ vnics | length }}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
Storage - {{ disks | length }} Disk(s)
66 |
67 | {% for disk in disks %} 68 |
69 |
70 | {{ disk.name }} 71 |
72 |
{{ disk.thin|yesno:"Thin-provisioning, Thick-provisioning" }}
73 |
{{ disk.size | multiply:1048576 | filesizeformat }}
74 |
75 | {% if not disk.raw %} 76 | {{ disk.datastore.name }} 78 | {% else %} 79 | RAW Device 80 | {% endif %} 81 |
82 |
83 | {% endfor %} 84 |
85 |
86 |
87 |
Network - {{ vnics | length }} Network(s)
88 |
89 | {% for vnic in vnics %} 90 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 |
Network{{ vnic.network.name }}
Interface{{ vnic.name }}
MAC{{ vnic.mac }}
Driver{{ vnic.driver }}
Vlan ID{{ vnic.network.vlanId }}
113 |
114 | {% endfor %} 115 |
116 |
117 |
118 |
119 | {% endblock %} -------------------------------------------------------------------------------- /vsphere/templates/resourcepool.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load multiply %} 3 | {% load divide %} 4 | {% load percent %} 5 | {% block title %} 6 | vSphere - {{ data.name }} 7 | {% endblock %} 8 | {% block content %} 9 |
Statistics for Resource Pool {{ data.name }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
Total
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
Memory{{ data.memory_sum | multiply:1048576 | filesizeformat }}
Storage{{ data.storage_sum | multiply:1048576 | filesizeformat }}
vCPU{{ data.vcpu_sum }}
33 |
34 |
35 |
Averages
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
MinimumMaxiumAverageDeviation
Memory{{ data.memory_min | multiply:1048576 | filesizeformat }}{{ data.memory_max | multiply:1048576 | filesizeformat }}{{ data.memory_avg | multiply:1048576 | filesizeformat }}{{ data.memory_dev | floatformat }}
Storage{{ data.storage_min | multiply:1048576 | filesizeformat }}{{ data.storage_max | multiply:1048576 | filesizeformat }}{{ data.storage_avg | multiply:1048576 | filesizeformat }}{{ data.storage_dev | floatformat }}
vCPU{{ data.vcpu_min }}{{ data.vcpu_max }}{{ data.vcpu_avg | floatformat }}{{ data.vcpu_dev | floatformat }}
69 |
70 | 71 |
72 |
73 | 74 |
75 |
Guest - {{ data.guests | length }} vm(s)
76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {% for guest in data.guests %} 89 | 90 | 91 | 102 | 103 | 104 | 105 | 106 | 107 | {% endfor %} 108 |
HostnameStatusMemoryDiskvCPUDatacenter
{{ guest.name }} 92 | {% if guest.poweredOn %} 93 | 94 | 95 | 96 | {% else %} 97 | 98 | 99 | 100 | {% endif %} 101 | {{ guest.memory | multiply:1048576 | filesizeformat }}{{ guest.get_disk_reserved | multiply:1048576 | filesizeformat }}{{ guest.vcpu }}{{ guest.hypervisor.datacenter }}
109 |
110 | 111 | 112 | 153 | {% endblock %} 154 | -------------------------------------------------------------------------------- /vsphere/models.py: -------------------------------------------------------------------------------- 1 | from django.core.validators import RegexValidator 2 | from django.db.models import Sum 3 | from django.db import models 4 | import re 5 | 6 | name_regex = re.compile(r'^[a-zA-Z0-9\-\_\.]+$') 7 | 8 | 9 | class Hypervisor(models.Model): 10 | name = models.CharField(max_length=100, unique=True, 11 | validators=[RegexValidator(regex=name_regex)]) 12 | cpuModel = models.CharField(max_length=100, blank=True, null=True) 13 | numCpuPkgs = models.IntegerField() 14 | vendor = models.CharField(max_length=100, blank=True, null=True) 15 | model = models.CharField(max_length=100, blank=True, null=True) 16 | numCpuThreads = models.IntegerField() 17 | memorySize = models.IntegerField() 18 | numCpuCores = models.IntegerField() 19 | cpuMhz = models.IntegerField() 20 | numHBAs = models.IntegerField() 21 | numNics = models.IntegerField() 22 | productName = models.CharField(max_length=100, blank=True, null=True) 23 | productVersion = models.CharField(max_length=20, blank=True, null=True) 24 | annotation = models.CharField(max_length=100, blank=True, null=True) 25 | datacenter = models.CharField(max_length=100, unique=False, 26 | validators=[RegexValidator(regex=name_regex)]) 27 | 28 | def __unicode__(self): 29 | return u'%s' % self.name 30 | 31 | def get_number_guest(self): 32 | guest_list = Guest.objects.filter(hypervisor=self) 33 | return len(guest_list) 34 | 35 | def get_datastores_size(self): 36 | ds_list = Datastore.objects.filter(hypervisor=self) 37 | capacity = 0 38 | for ds in ds_list: 39 | capacity += int(ds.capacity) 40 | return capacity 41 | 42 | def get_mem_reserved(self): 43 | guest_list = Guest.objects.filter(hypervisor=self) 44 | mem_reserved = 0 45 | for guest in guest_list: 46 | mem_reserved += int(guest.memory) 47 | return mem_reserved 48 | memoryReserved = property(get_mem_reserved) 49 | 50 | def get_disk_reserved(self): 51 | ds_list = Datastore.objects.filter(hypervisor=self) 52 | disk_reserved = 0 53 | for ds in ds_list: 54 | disk_reserved += Datastore.get_reserved(ds) 55 | return disk_reserved 56 | diskReserved = property(get_disk_reserved) 57 | 58 | def get_vcpu_reserved(self): 59 | guest_list = Guest.objects.filter(hypervisor=self) 60 | vcpu_reserved = 0 61 | for guest in guest_list: 62 | vcpu_reserved += int(guest.vcpu) 63 | return vcpu_reserved 64 | vcpuReserved = property(get_vcpu_reserved) 65 | 66 | def get_raw_disk_reserved(self): 67 | raw_disk_reserved = 0 68 | guest_list = Guest.objects.filter(hypervisor=self) 69 | for guest in guest_list: 70 | try: 71 | _temp = Disk.objects.filter(guest=guest, raw=True).aggregate(Sum('size')) 72 | raw_disk_reserved += _temp.get('size__sum') 73 | except: 74 | pass 75 | return raw_disk_reserved 76 | 77 | 78 | class Guest(models.Model): 79 | name = models.CharField(max_length=100, unique=True) 80 | poweredOn = models.BooleanField() 81 | vcpu = models.IntegerField() 82 | memory = models.IntegerField() 83 | resourcePool = models.CharField(max_length=100) 84 | annotation = models.CharField(max_length=100, blank=True, null=True) 85 | osVersion = models.CharField(max_length=100, blank=True, null=True) 86 | hypervisor = models.ForeignKey(Hypervisor, unique=False, null=False) 87 | 88 | def __unicode__(self): 89 | return u'%s.%s' % (self.hypervisor.name, self.name) 90 | 91 | def get_hypervisor_name(self): 92 | return self.hypervisor.name 93 | 94 | def get_disk_reserved(self, raw=''): 95 | if raw is False: 96 | disk_list = Disk.objects.filter(guest=self, raw=False) 97 | else: 98 | disk_list = Disk.objects.filter(guest=self) 99 | disk_reserved = 0 100 | for disk in disk_list: 101 | disk_reserved += int(disk.size) 102 | return disk_reserved 103 | 104 | 105 | class Datastore(models.Model): 106 | name = models.CharField(max_length=100) 107 | capacity = models.IntegerField() 108 | hypervisor = models.ForeignKey(Hypervisor, null=False) 109 | 110 | def __unicode__(self): 111 | return u'%s.%s' % (self.hypervisor.name, self.name) 112 | 113 | def get_reserved(self): 114 | vd_list = Disk.objects.filter(datastore=self) 115 | reserved = 0 116 | for vd in vd_list: 117 | if not vd.raw: 118 | reserved += int(vd.size) 119 | return reserved 120 | 121 | def get_guests(self): 122 | disk_list = Disk.objects.filter(datastore=self) 123 | list_guest = [] 124 | for disk in disk_list: 125 | if disk.guest not in list_guest: 126 | list_guest.append(disk.guest) 127 | return list_guest 128 | 129 | 130 | class Disk(models.Model): 131 | name = models.CharField(max_length=100) 132 | size = models.IntegerField() 133 | guest = models.ForeignKey(Guest, null=False) 134 | datastore = models.ForeignKey(Datastore, null=True) 135 | raw = models.BooleanField(default=False) 136 | thin = models.BooleanField(default=False) 137 | 138 | def __unicode__(self): 139 | return u'%s' % self.name 140 | 141 | 142 | class Vswitch(models.Model): 143 | name = models.CharField(max_length=100) 144 | hypervisor = models.ForeignKey(Hypervisor, unique=False, null=False) 145 | 146 | def __unicode__(self): 147 | return u'%s' % self.name 148 | 149 | 150 | class Network(models.Model): 151 | name = models.CharField(max_length=100) 152 | vlanId = models.IntegerField() 153 | vswitch = models.ForeignKey(Vswitch, unique=False, null=False) 154 | 155 | def __unicode__(self): 156 | return u'%s.%s' % (self.vswitch, self.name) 157 | 158 | def get_virtualnics(self): 159 | vnic_list = VirtualNic.objects.filter(network=self) 160 | return vnic_list 161 | 162 | 163 | class Interface(models.Model): 164 | name = models.CharField(max_length=100) 165 | mac = models.CharField(max_length=100, blank=True, null=True) 166 | driver = models.CharField(max_length=100, blank=True, null=True) 167 | linkSpeed = models.IntegerField() 168 | vswitch = models.ForeignKey(Vswitch, unique=False, null=True) 169 | hypervisor = models.ForeignKey(Hypervisor, unique=False, null=False) 170 | 171 | def __unicode__(self): 172 | return u'%s.%s' % (self.hypervisor.name, self.name) 173 | 174 | 175 | class VirtualNic(models.Model): 176 | name = models.CharField(max_length=100) 177 | mac = models.CharField(max_length=100, blank=True, null=True) 178 | driver = models.CharField(max_length=100, blank=True, null=True) 179 | guest = models.ForeignKey(Guest, unique=False, null=False) 180 | network = models.ForeignKey(Network, unique=False, null=False) 181 | 182 | def __unicode__(self): 183 | return u'%s.%s' % (self.guest.name, self.name) 184 | -------------------------------------------------------------------------------- /vsphere/templates/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 37 | 38 | 65 | 66 | {% block title %}{% endblock %} 67 | 68 | 69 | 70 | 71 | 113 | 114 | 141 | 142 | 143 |
144 |
145 |
146 | {% block content %}{% endblock %} 147 |
148 |
149 |
150 | 151 | 152 | -------------------------------------------------------------------------------- /vsphere/templates/top10.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% block title %} 3 | vSphere - Top 10 Resource Pool 4 | {% endblock %} 5 | {% block content %} 6 |
7 |

Top 10 resources pool

8 |
9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 242 | {% endblock %} 243 | -------------------------------------------------------------------------------- /vsphere/templates/datacenter.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load divide %} 3 | 4 | {% block title %} 5 | vSphere - Datacenter Vision 6 | {% endblock %} 7 | {% block content %} 8 | 9 |
Statistics by Datacenter
10 | {% for datacenter in data %} 11 |
12 |
13 |
14 |
15 | Statistics for {{ datacenter.datacenter }} 16 |
17 |
18 | {{ datacenter.hypervisor }} Hypervisors 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 281 | {% endfor%} 282 | {% endblock %} 283 | -------------------------------------------------------------------------------- /vsphere/templates/stats_by_resourcepool.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% block title %} 3 | vSphere - By Resource Pool 4 | {% endblock %} 5 | {% block content %} 6 |
7 |

Reserved resources by Resource Pool

8 |
9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 294 | {% endblock %} 295 | -------------------------------------------------------------------------------- /vsphere/templates/hypervisor.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% load multiply %} 3 | {% load percent %} 4 | {% block title %} 5 | vSphere - {{ hypervisor.name }} 6 | {% endblock %} 7 | {% block content %} 8 |

9 | {{ hypervisor.name }} 10 |

11 |
12 |
13 |
14 |
General
15 |
16 |
17 |
Datacenter
18 |
{{ hypervisor.datacenter }}
19 |
20 |
21 |
Manufacturer
22 |
{{ hypervisor.vendor }}
23 |
24 |
25 |
Model
26 |
{{ hypervisor.model }}
27 |
28 |
29 |
CPU Cores
30 |
{{ hypervisor.numCpuCores }} CPUs x {{ hypervisor.cpuMhz }} MHz
31 |
32 |
33 |
Processor Type
34 |
{{ hypervisor.cpuModel }}
35 |
36 |
37 |
Processor Sockets
38 |
{{ hypervisor.numCpuPkgs }}
39 |
40 |
41 |
Cores per Socket
42 |
{{ hypervisor.numCpuCores }}
43 |
44 |
45 |
Logical Processors
46 |
{{ hypervisor.numCpuThreads }}
47 |
48 |
49 |
Number of NICs
50 |
{{ hypervisor.numNics }}
51 |
52 |
53 |
Number of HBAs
54 |
{{ hypervisor.numHBAs }}
55 |
56 |
57 |
Product
58 |
{{ hypervisor.productName }} - {{ hypervisor.productVersion }}
59 |
60 |
61 |
Notes
62 |
{{ hypervisor.annotation }}
63 |
64 |
65 |
66 |
67 |
68 |
69 |
Resources
70 |
71 |
72 | Memory: {{ hypervisor.memoryReserved | multiply:1048576 | filesizeformat }} 73 | / {{ hypervisor.memorySize | multiply:1048576 | filesizeformat }} 74 |
75 |
76 |
77 |
78 |
{{ hypervisor.memoryReserved|percent:hypervisor.memorySize }}%
85 |
86 |
87 |
88 |
89 |
90 | Datastores: {{ hypervisor.diskReserved | multiply:1048576 | filesizeformat }} 91 | / {{ hypervisor.get_datastores_size | multiply:1048576 | filesizeformat }} 92 |
93 |
94 |
95 |
96 |
{{ hypervisor.diskReserved|percent:hypervisor.get_datastores_size }}%
103 |
104 |
105 |
106 |
107 |
108 | 109 | 110 |
111 |
Interfaces - {{ interfaces | length }} Interfaces(s)
112 |
113 |
114 | {% for int in interfaces %} 115 |
116 | {{ int.name }} 117 |
118 | {% endfor %} 119 |
120 |
121 |
122 | 123 |
124 |
125 | 126 |
127 |
Storage - {{ datastores | length }} Datastore(s)
128 |
129 | {% for ds in datastores %} 130 |
131 |
132 | {{ ds.name }} 133 |
134 |
135 |
{{ ds.capacity | multiply:1048576 | filesizeformat }} Total
136 |
{{ ds.get_reserved | multiply:1048576 | filesizeformat }} Used 137 |
138 | 143 |
144 |
145 |
{{ ds.get_reserved|percent:ds.capacity }}%
152 |
153 |
154 |
155 | {% endfor %} 156 | {% if hypervisor.get_raw_disk_reserved %} 157 |
158 |
159 |
160 | Total Raw Space 161 |
162 |
163 |
{{ hypervisor.get_raw_disk_reserved | multiply:1048576 | filesizeformat }}
164 |
165 |
166 |
167 |
168 | {% endif %} 169 |
170 |
171 | 172 |
173 |
Network - {{ networks | length }} Network(s)
174 |
175 | 176 | {% for vs in vswitchs %} 177 | 178 | 180 | 201 | 202 | {% endfor %} 203 |
179 | {{ vs.name }} 181 |
182 | {% for net in networks %} 183 | {% if net.vswitch.name == vs.name %} 184 |
185 |
186 |
{{ net.name }}
187 |
188 | {% if net.vlanId %} 189 | vlan: {{ net.vlanId }} 190 | {% endif %} 191 |
192 |
193 | 196 |
197 | {% endif %} 198 | {% endfor %} 199 |
200 |
204 |
205 |
206 | 207 |
208 |
Guest - {{ hypervisor.get_number_guest }} vm(s)
209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | {% for guest in guests %} 222 | 223 | 224 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | {% endfor %} 242 |
HostnameStatusResource PoolMemoryDiskvCPU
{{ guest.name }} 225 | {% if guest.poweredOn %} 226 | 227 | 228 | 229 | {% else %} 230 | 231 | 232 | 233 | {% endif %} 234 | {{ guest.resourcePool }}{{ guest.memory | multiply:1048576 | filesizeformat }}{{ guest.get_disk_reserved | multiply:1048576 | filesizeformat }}{{ guest.vcpu }}
243 |
244 | {% endblock %} -------------------------------------------------------------------------------- /vsphere/templates/physical.html: -------------------------------------------------------------------------------- 1 | {% extends "head.html" %} 2 | {% block title %} 3 | vSphere - Global Vision 4 | {% endblock %} 5 | {% block content %} 6 |
Physical resources installed
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {% if is_raw %} 24 |
25 |
26 |
27 |
28 |
29 | {% endif %} 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 47 | 371 | {% endblock %} 372 | -------------------------------------------------------------------------------- /tools/import_hypervisor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pysphere import VIServer, MORTypes, VIProperty 3 | from optparse import OptionParser 4 | from vsphere.models import VirtualNic 5 | from vsphere.models import Hypervisor 6 | from vsphere.models import Datastore 7 | from vsphere.models import Interface 8 | from vsphere.models import Vswitch 9 | from vsphere.models import Network 10 | from vsphere.models import Guest 11 | from vsphere.models import Disk 12 | from os.path import expanduser 13 | from sys import exit 14 | import ConfigParser 15 | import re 16 | 17 | 18 | def vmware_connect(host, user, password): 19 | """Return a VIServer conneciont""" 20 | s = VIServer() 21 | s.connect(host, user, password) 22 | return s 23 | 24 | 25 | def get_disks(vm): 26 | """Return a list of disk dict with keys name, size, thin, raw, filename""" 27 | ret_val = [] 28 | 29 | disks = [d for d in vm.properties.config.hardware.device 30 | if d._type == 'VirtualDisk' and d.backing._type in 31 | ['VirtualDiskFlatVer1BackingInfo', 32 | 'VirtualDiskFlatVer2BackingInfo', 33 | 'VirtualDiskRawDiskMappingVer1BackingInfo', 34 | 'VirtualDiskSparseVer1BackingInfo', 35 | 'VirtualDiskSparseVer2BackingInfo']] 36 | 37 | for disk in disks: 38 | name = disk.deviceInfo.label 39 | size = int(disk.deviceInfo.summary.replace(',', '').replace(' KB', '')) 40 | size /= 1024 41 | filename = disk.backing.fileName 42 | raw = thin = False 43 | if disk.backing._type == 'VirtualDiskRawDiskMappingVer1BackingInfo': 44 | raw = True 45 | if hasattr(disk.backing, "thinProvisioned"): 46 | thin = True 47 | ret_val.append({'name': name, 'size': size, 'thin': thin, 48 | 'raw': raw, 'filename': filename}) 49 | return ret_val 50 | 51 | 52 | def get_datastores(server): 53 | """Return a list of datastore dict with keys name, capacity, freeSpace""" 54 | ret_val = [] 55 | for ds_mor, name in server.get_datastores().items(): 56 | props = VIProperty(server, ds_mor) 57 | capacity = props.summary.capacity / 1024 / 1024 58 | freeSpace = props.summary.freeSpace / 1024 / 1024 59 | ret_val.append({'name': name, 60 | 'capacity': capacity, 61 | 'freeSpace': freeSpace}) 62 | return ret_val 63 | 64 | 65 | def get_networks(server): 66 | """Return a list a network dict with keys name, vlanId, vswitchName""" 67 | mor, name = server.get_hosts().items()[0] 68 | prop = VIProperty(server, mor) 69 | ret_val = [] 70 | for network in prop.configManager.networkSystem.networkInfo.portgroup: 71 | name = network.spec.name 72 | vlanId = network.spec.vlanId 73 | vswitchName = network.spec.vswitchName 74 | ret_val.append({'name': name, 75 | 'vlanId': vlanId, 76 | 'vswitchName': vswitchName}) 77 | 78 | return ret_val 79 | 80 | 81 | def get_interfaces(prop): 82 | """Return a list of interface dict with keys driver, linkSpeed, mac, 83 | name""" 84 | ret_val = [] 85 | for interface in prop.configManager.networkSystem.networkInfo.pnic: 86 | name = interface.device 87 | mac = interface.mac 88 | driver = interface.driver 89 | try: 90 | linkSpeed = interface.linkSpeed.speedMb 91 | except: 92 | try: 93 | linkSpeed = interface.spec.linkSpeed.speedMb 94 | except: 95 | linkSpeed = 0 96 | ret_val.append({'name': name, 97 | 'mac': mac, 98 | 'driver': driver, 99 | 'linkSpeed': linkSpeed}) 100 | 101 | return ret_val 102 | 103 | 104 | def get_vnics(vm): 105 | """Return a list of vnic dict with keys name, mac, type, network""" 106 | ret_val = [] 107 | for v in vm.get_property("devices").values(): 108 | if v.get('macAddress'): 109 | name = v.get('label') 110 | mac = v.get('macAddress') 111 | driver = v.get('type') 112 | network = v.get('summary') 113 | ret_val.append({'name': name, 114 | 'mac': mac, 115 | 'type': driver, 116 | 'network': network}) 117 | 118 | return ret_val 119 | 120 | 121 | def get_vswitch(server): 122 | """Return a list of vswitch dict with keys name, nicDevice""" 123 | mor, name = server.get_hosts().items()[0] 124 | prop = VIProperty(server, mor) 125 | ret_val = [] 126 | for v in prop.configManager.networkSystem.networkConfig.vswitch: 127 | name = v.name 128 | nicDevice = v.spec.bridge.nicDevice # this is a list 129 | ret_val.append({'name': name, 'nicDevice': nicDevice}) 130 | 131 | return ret_val 132 | 133 | 134 | def get_guests(server): 135 | """Return a list of dict guest""" 136 | 137 | properties = [ 138 | 'name', 139 | 'storage.perDatastoreUsage', 140 | 'config.hardware.memoryMB', 141 | 'config.hardware.numCPU' 142 | ] 143 | results = server._retrieve_properties_traversal( 144 | property_names=properties, 145 | obj_type=MORTypes.VirtualMachine 146 | ) 147 | 148 | ret_val = [] 149 | if not results: 150 | return ret_val 151 | 152 | for item in results: 153 | for p in item.PropSet: 154 | if p.Name == 'config.hardware.memoryMB': 155 | memory = int(p.Val) 156 | if p.Name == 'config.hardware.numCPU': 157 | vcpu = int(p.Val) 158 | if p.Name == 'name': 159 | name = p.Val 160 | try: 161 | vm = server.get_vm_by_name(name) 162 | except: 163 | poweredOn = False 164 | disks = {} 165 | osVersion = '' 166 | annotation = '' 167 | vnics = [] 168 | resourcePool = 'Default' 169 | else: 170 | annotation = vm.properties.config.annotation 171 | osVersion = vm.get_property('guest_full_name') 172 | poweredOn = vm.is_powered_on() 173 | disks = get_disks(vm) 174 | vnics = get_vnics(vm) 175 | resourcePool = vm.get_resource_pool_name() 176 | 177 | ret_val.append({'name': name, 178 | 'vcpu': vcpu, 179 | 'memory': memory, 180 | 'disks': disks, 181 | 'vnics': vnics, 182 | 'poweredOn': poweredOn, 183 | 'resourcePool': resourcePool, 184 | 'annotation': annotation, 185 | 'osVersion': osVersion}) 186 | 187 | return ret_val 188 | 189 | 190 | def get_hardware(server): 191 | """Return a hardware dict""" 192 | 193 | mor, name = server.get_hosts().items()[0] 194 | prop = VIProperty(server, mor) 195 | overallMemoryUsage = prop.summary.quickStats.overallMemoryUsage 196 | overallCpuUsage = prop.summary.quickStats.overallCpuUsage 197 | numCpuThreads = prop.summary.hardware.numCpuThreads 198 | productName = prop.summary.config.product.name 199 | productVersion = prop.summary.config.product.version 200 | numCpuCores = prop.summary.hardware.numCpuCores 201 | numCpuPkgs = prop.summary.hardware.numCpuPkgs 202 | cpuModel = prop.summary.hardware.cpuModel 203 | numHBAs = prop.summary.hardware.numHBAs 204 | numNics = prop.summary.hardware.numNics 205 | cpuMhz = prop.summary.hardware.cpuMhz 206 | memorySize = prop.hardware.memorySize 207 | vendor = prop.summary.hardware.vendor 208 | model = prop.summary.hardware.model 209 | interfaces = get_interfaces(prop) 210 | datastores = get_datastores(server) 211 | 212 | return { 213 | 'name': name, 214 | 'overallCpuUsage': overallCpuUsage, 215 | 'overallMemoryUsage': overallMemoryUsage, 216 | 'memorySize': memorySize / 1024 / 1024, 217 | 'cpuModel': ' '.join(cpuModel.split()), 218 | 'vendor': vendor, 219 | 'numCpuPkgs': numCpuPkgs, 220 | 'numCpuCores': numCpuCores, 221 | 'numCpuThreads': numCpuThreads, 222 | 'productName': productName, 223 | 'productVersion': productVersion, 224 | 'numNics': numNics, 225 | 'numHBAs': numHBAs, 226 | 'model': model, 227 | 'cpuMhz': cpuMhz, 228 | 'interfaces': interfaces, 229 | 'datastores': datastores, 230 | } 231 | 232 | 233 | def create_hypervisor(hypervisor, datacenter, note=''): 234 | """Create an Hypervisor object from get_hardware and a datacenter""" 235 | h = Hypervisor() 236 | h.name = hypervisor.get('name') 237 | h.cpuModel = hypervisor.get('cpuModel') 238 | h.numCpuPkgs = hypervisor.get('numCpuPkgs') 239 | h.vendor = hypervisor.get('vendor') 240 | h.numCpuThreads = hypervisor.get('numCpuThreads') 241 | h.memorySize = hypervisor.get('memorySize') 242 | h.numCpuCores = hypervisor.get('numCpuCores') 243 | h.cpuMhz = hypervisor.get('cpuMhz') 244 | h.numHBAs = hypervisor.get('numHBAs') 245 | h.numNics = hypervisor.get('numNics') 246 | h.productName = hypervisor.get('productName') 247 | h.productVersion = hypervisor.get('productVersion') 248 | h.annotation = note 249 | h.datacenter = datacenter 250 | 251 | h.clean() 252 | h.save() 253 | return h 254 | 255 | 256 | def create_datastore(datastore, hypervisor): 257 | """ Create a Datastore object from get_datastores and Hypervisor object""" 258 | d = Datastore() 259 | d.name = datastore.get('name') 260 | d.capacity = datastore.get('capacity') 261 | d.hypervisor_id = hypervisor.id 262 | 263 | d.clean() 264 | d.save() 265 | return d 266 | 267 | 268 | def create_guest(guest, hypervisor): 269 | """Create Guest object from get_guests and Hypervisor object""" 270 | g = Guest() 271 | g.name = guest.get('name') 272 | g.memory = guest.get('memory') 273 | g.poweredOn = guest.get('poweredOn') 274 | g.vcpu = guest.get('vcpu') 275 | g.resourcePool = guest.get('resourcePool') 276 | g.annotation = guest.get('annotation') 277 | g.osVersion = guest.get('osVersion') 278 | g.hypervisor = hypervisor 279 | 280 | g.clean() 281 | g.save() 282 | return g 283 | 284 | 285 | def create_disk(disk, datastore, guest): 286 | """Create a Disk object from get_disks, Datastore and Guest objects""" 287 | d = Disk() 288 | d.name = disk.get('name') 289 | d.size = disk.get('size') 290 | d.raw = disk.get('raw') 291 | d.guest = guest 292 | if not d.raw: 293 | d.datastore = datastore 294 | 295 | d.clean() 296 | d.save() 297 | return d 298 | 299 | 300 | def create_vswitch(vswitch, hypervisor): 301 | """Create a Vswitch object from get_vswitch""" 302 | v = Vswitch() 303 | v.name = vswitch.get('name') 304 | v.hypervisor = hypervisor 305 | 306 | v.clean() 307 | v.save() 308 | return v 309 | 310 | 311 | def create_network(network, vswitch): 312 | """Create a Network object from get_networks and a vswitch object""" 313 | n = Network() 314 | n.name = network.get('name') 315 | n.vlanId = network.get('vlanId') 316 | n.vswitch = vswitch 317 | 318 | n.clean() 319 | n.save() 320 | return n 321 | 322 | 323 | def create_interface(interface, hypervisor, vswitch=None): 324 | """Create an Interface object from get_interfaces, 325 | an Hypervisor and Vswitch objects""" 326 | i = Interface() 327 | i.name = interface.get('name') 328 | i.mac = interface.get('mac') 329 | i.driver = interface.get('driver') 330 | i.linkSpeed = interface.get('linkSpeed') 331 | if vswitch: 332 | i.vswitch = vswitch 333 | i.hypervisor = hypervisor 334 | 335 | i.clean() 336 | i.save() 337 | return i 338 | 339 | 340 | def create_vnic(vnic, guest, network): 341 | """Create a VirtualNic object from get_vnics, an Guest 342 | and a Network object""" 343 | v = VirtualNic() 344 | v.name = vnic.get('name') 345 | v.mac = vnic.get('mac') 346 | v.driver = vnic.get('type') 347 | v.guest = guest 348 | v.network = network 349 | 350 | v.clean() 351 | v.save() 352 | return v 353 | 354 | 355 | def create_full_hypervisor(server, datacenter, note): 356 | """Create a full hypervisor with Hardware, interface, network, 357 | guetst, ...""" 358 | HARDWARE = get_hardware(server) 359 | hypervisor = create_hypervisor(HARDWARE, datacenter, note) 360 | 361 | DATASTORES = HARDWARE.get('datastores') 362 | for ds in DATASTORES: 363 | create_datastore(ds, hypervisor) 364 | 365 | NETWORKS = get_networks(server) 366 | VSWITCHS = get_vswitch(server) 367 | for vswitch in VSWITCHS: 368 | create_vswitch(vswitch, hypervisor) 369 | for network in NETWORKS: 370 | n_vswitchName = network.get('vswitchName') 371 | n_vswitch = Vswitch.objects.get(name=n_vswitchName, 372 | hypervisor=hypervisor) 373 | create_network(network, n_vswitch) 374 | 375 | INTERFACES = HARDWARE.get('interfaces') 376 | for interface in INTERFACES: 377 | i_vs_name = '' 378 | i_name = interface.get('name') 379 | for x in VSWITCHS: 380 | if i_name in x.get('nicDevice'): 381 | i_vs_name = x.get('name') 382 | try: 383 | n_vswitch = Vswitch.objects.get(name=i_vs_name, 384 | hypervisor=hypervisor) 385 | except: 386 | create_interface(interface, hypervisor) 387 | else: 388 | create_interface(interface, hypervisor, n_vswitch) 389 | 390 | GUESTS = get_guests(server) 391 | for guest in GUESTS: 392 | g = create_guest(guest, hypervisor) 393 | 394 | DISKS = guest.get('disks') 395 | for disk in DISKS: 396 | disk_name = disk.get('filename') 397 | m = re.search(r"\[([A-Za-z0-9_]+)\]", disk_name) 398 | try: 399 | disk_ds_name = m.group(1) 400 | disk_ds = Datastore.objects.get(name=disk_ds_name, 401 | hypervisor=hypervisor) 402 | create_disk(disk, disk_ds, g) 403 | except: 404 | pass 405 | 406 | VNICS = guest.get('vnics') 407 | for vnic in VNICS: 408 | vnic_net_name = vnic.get('network') 409 | try: 410 | vnic_net = Network.objects.get(name=vnic_net_name, 411 | vswitch__hypervisor=hypervisor) 412 | create_vnic(vnic, g, vnic_net) 413 | except: 414 | pass 415 | 416 | 417 | if __name__ == '__main__': 418 | Config = ConfigParser.ConfigParser() 419 | Config.read(expanduser('~/.hypervisor.ini')) 420 | 421 | parser = OptionParser() 422 | parser.add_option('-H', '--hostname', 423 | help='Host name, IP Address - mandatory') 424 | parser.add_option('-u', '--user', default='root', 425 | help='User name [default: %default]') 426 | parser.add_option('-d', '--datacenter', default='', 427 | help='Datacenter [default: %default]') 428 | parser.add_option('-p', '--password', type='string', default='', 429 | help='Password to connect [default: %default]') 430 | parser.add_option('-a', '--anotation', type='string', default='', 431 | help='Hypervisor Note [default: %default]') 432 | 433 | (options, args) = parser.parse_args() 434 | 435 | mandatories = ['hostname', 'datacenter'] 436 | 437 | for m in mandatories: 438 | if getattr(options, m) is None: 439 | print '"%s" option is missing' % m 440 | parser.print_help() 441 | exit(-1) 442 | 443 | try: 444 | password = Config.get(options.user, 'password') 445 | except: 446 | password = '' 447 | 448 | if getattr(options, 'password'): 449 | password = options.password 450 | if not password: 451 | print '"password" option is missing' 452 | exit(-1) 453 | 454 | user = options.user 455 | hostname = options.hostname 456 | datacenter = options.datacenter 457 | note = options.anotation 458 | 459 | try: 460 | server = vmware_connect(hostname, user, password) 461 | except: 462 | exit(1) 463 | 464 | create_full_hypervisor(server, datacenter, note) 465 | 466 | server.disconnect() 467 | -------------------------------------------------------------------------------- /vsphere/views.py: -------------------------------------------------------------------------------- 1 | from vsphere.models import Hypervisor, Guest, Datastore, Disk, Interface, VirtualNic, Network, Vswitch 2 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 3 | from django.shortcuts import render_to_response 4 | from django.db.models import Min, Max, Avg, Sum 5 | from django.template import RequestContext 6 | from django.http import HttpResponse 7 | 8 | 9 | def list_deviation(l): 10 | import math 11 | 12 | def average(s): 13 | return sum(s) * 1.0 / len(s) 14 | avg = average(l) 15 | variance = map(lambda x: (x - avg)**2, l) 16 | 17 | standard_deviation = math.sqrt(average(variance)) 18 | return standard_deviation 19 | 20 | 21 | def home(request): 22 | hypervisor_list = Hypervisor.objects.all() 23 | datacenter = [x.datacenter for x in hypervisor_list] 24 | datacenter = list(set(datacenter)) 25 | 26 | ret_val = [] 27 | for dc in datacenter: 28 | hypervisors = Hypervisor.objects.filter(datacenter=dc) 29 | num_vm = 0 30 | storage = 0 31 | storage_reserved = 0 32 | mem_reserved = 0 33 | mem = 0 34 | threads = 0 35 | vcpu_reserved = 0 36 | raw_storage_reserved = 0 37 | for hv in hypervisors: 38 | num_vm += hv.get_number_guest() 39 | storage += hv.get_datastores_size() 40 | storage_reserved += hv.diskReserved 41 | mem_reserved += hv.memoryReserved 42 | mem += hv.memorySize 43 | threads += hv.numCpuThreads 44 | vcpu_reserved += hv.vcpuReserved 45 | raw_storage_reserved += hv.get_raw_disk_reserved() 46 | 47 | used_storage = int(100 * storage_reserved / storage) 48 | used_mem = int(100 * mem_reserved / mem) 49 | cpu_consolidation = float(vcpu_reserved) / threads 50 | 51 | info = {'name': dc, 52 | 'nb_hv': len(hypervisors), 53 | 'nb_guest': num_vm, 54 | 'used_storage': used_storage, 55 | 'used_mem': used_mem, 56 | 'cpu_consolidation': cpu_consolidation, 57 | 'raw_storage_reserved': raw_storage_reserved, 58 | } 59 | ret_val.append(info) 60 | 61 | data = { 62 | 'datacenter': ret_val, 63 | } 64 | return render_to_response('home.html', 65 | data, 66 | context_instance=RequestContext(request)) 67 | 68 | 69 | def resourcepools_info(request): 70 | guest_list = Guest.objects.all() 71 | resource_pool = [x.resourcePool for x in guest_list] 72 | resource_pool = list(set(resource_pool)) 73 | 74 | ret_val = [] 75 | for rp in resource_pool: 76 | guests = Guest.objects.filter(resourcePool=rp) 77 | num_vm = len(guests) 78 | mem_reserved = 0 79 | vcpu_reserved = 0 80 | disk_reserved = 0 81 | for guest in guests: 82 | mem_reserved += guest.memory 83 | vcpu_reserved += guest.vcpu 84 | disk_reserved += guest.get_disk_reserved(raw=False) 85 | info = {'name': rp, 86 | 'num_vm': num_vm, 87 | 'mem_reserved': mem_reserved, 88 | 'vcpu_reserved': vcpu_reserved, 89 | 'disk_reserved': disk_reserved, 90 | } 91 | ret_val.append(info) 92 | 93 | data = { 94 | 'resource_pool': ret_val, 95 | } 96 | return data 97 | 98 | 99 | def stats_by_resourcepool(request): 100 | data = resourcepools_info(request) 101 | return render_to_response('stats_by_resourcepool.html', 102 | data, 103 | context_instance=RequestContext(request)) 104 | 105 | 106 | def top10(request): 107 | data = resourcepools_info(request) 108 | d = data['resource_pool'] 109 | data['top10_mem'] = [x['name'] for x in sorted(d, key=lambda a: -a['mem_reserved'])][:10] 110 | data['top10_cpu'] = [x['name'] for x in sorted(d, key=lambda a: -a['vcpu_reserved'])][:10] 111 | data['top10_disk'] = [x['name'] for x in sorted(d, key=lambda a: -a['disk_reserved'])][:10] 112 | 113 | return render_to_response('top10.html', 114 | data, 115 | context_instance=RequestContext(request)) 116 | 117 | 118 | def resourcepool_info(rp): 119 | 120 | guest_list = Guest.objects.filter(resourcePool=rp) 121 | 122 | memory_max = guest_list.filter(resourcePool=rp).aggregate(Max('memory')) 123 | memory_min = guest_list.filter(resourcePool=rp).aggregate(Min('memory')) 124 | memory_avg = guest_list.filter(resourcePool=rp).aggregate(Avg('memory')) 125 | memory_sum = guest_list.filter(resourcePool=rp).aggregate(Sum('memory')) 126 | memory_dev = list_deviation([x.memory for x in guest_list]) 127 | vcpu_max = guest_list.filter(resourcePool=rp).aggregate(Max('vcpu')) 128 | vcpu_min = guest_list.filter(resourcePool=rp).aggregate(Min('vcpu')) 129 | vcpu_avg = guest_list.filter(resourcePool=rp).aggregate(Avg('vcpu')) 130 | vcpu_sum = guest_list.filter(resourcePool=rp).aggregate(Sum('vcpu')) 131 | vcpu_dev = list_deviation([x.vcpu for x in guest_list]) 132 | storage_list = [x.get_disk_reserved() for x in guest_list] 133 | storage_max = max(storage_list) 134 | storage_min = min(storage_list) 135 | storage_avg = sum(storage_list) / float(len(storage_list)) 136 | storage_sum = sum(storage_list) 137 | storage_dev = list_deviation(storage_list) 138 | 139 | dc_dict = {} 140 | for g in guest_list: 141 | dc = g.hypervisor.datacenter 142 | if dc not in dc_dict: 143 | dc_dict[dc] = 1 144 | else: 145 | dc_dict[dc] += 1 146 | dc_count_guest = [] 147 | for dc in dc_dict: 148 | dc_count_guest.append({'name': dc, 'count': dc_dict.get(dc)}) 149 | 150 | info = { 151 | 'name': rp, 152 | 'dc_count_guest': dc_count_guest, 153 | 'guests': guest_list, 154 | 'memory_max': memory_max.get('memory__max'), 155 | 'memory_min': memory_min.get('memory__min'), 156 | 'memory_avg': memory_avg.get('memory__avg'), 157 | 'memory_sum': memory_sum.get('memory__sum'), 158 | 'memory_dev': memory_dev, 159 | 'vcpu_max': vcpu_max.get('vcpu__max'), 160 | 'vcpu_min': vcpu_min.get('vcpu__min'), 161 | 'vcpu_avg': vcpu_avg.get('vcpu__avg'), 162 | 'vcpu_sum': vcpu_sum.get('vcpu__sum'), 163 | 'vcpu_dev': vcpu_dev, 164 | 'storage_max': storage_max, 165 | 'storage_min': storage_min, 166 | 'storage_avg': storage_avg, 167 | 'storage_sum': storage_sum, 168 | 'storage_dev': storage_dev, 169 | } 170 | 171 | return info 172 | 173 | 174 | def resourcepool(request): 175 | rp = request.GET.get('name') 176 | info = resourcepool_info(rp) 177 | data = { 178 | 'data': info, 179 | } 180 | return render_to_response('resourcepool.html', 181 | data, 182 | context_instance=RequestContext(request)) 183 | 184 | 185 | def resourcepool_info_list(rp): 186 | info = resourcepool_info(rp) 187 | return { 188 | 'name': rp, 189 | 'guests': info.get('guests'), 190 | 'memory_avg': info.get('memory_avg'), 191 | 'storage_avg': info.get('storage_avg'), 192 | 'vcpu_avg': info.get('vcpu_avg'), 193 | } 194 | 195 | 196 | def resourcepools(request): 197 | rp_list = [g.resourcePool for g in Guest.objects.all()] 198 | rp_list = list(set(rp_list)) 199 | 200 | data = [] 201 | for rp in rp_list: 202 | info = resourcepool_info_list(rp) 203 | data.append(info) 204 | 205 | paginator = Paginator(data, 25) 206 | 207 | page = request.GET.get('page') 208 | try: 209 | data_pag = paginator.page(page) 210 | except PageNotAnInteger: 211 | data_pag = paginator.page(1) 212 | except EmptyPage: 213 | data_pag = paginator.page(paginator.num_pages) 214 | 215 | data = { 216 | 'resourcepool_list': data_pag, 217 | } 218 | 219 | return render_to_response('resourcepools.html', 220 | data, 221 | context_instance=RequestContext(request)) 222 | 223 | 224 | def hypervisors(request): 225 | hypervisor_list = Hypervisor.objects.all() 226 | hypervisor_list_sorted = sorted(hypervisor_list, 227 | key=lambda a: (a.memoryReserved - a.memorySize, 228 | a.diskReserved - a.get_datastores_size())) 229 | paginator = Paginator(hypervisor_list_sorted, 25) 230 | 231 | page = request.GET.get('page') 232 | try: 233 | hypervisors_pag = paginator.page(page) 234 | except PageNotAnInteger: 235 | hypervisors_pag = paginator.page(1) 236 | except EmptyPage: 237 | hypervisors_pag = paginator.page(paginator.num_pages) 238 | 239 | data = { 240 | 'hypervisor_list': hypervisors_pag, 241 | } 242 | return render_to_response('hypervisors.html', 243 | data, 244 | context_instance=RequestContext(request)) 245 | 246 | 247 | def guests(request): 248 | guest_list = Guest.objects.all() 249 | guest_list = guest_list.order_by('name') 250 | paginator = Paginator(guest_list, 25) 251 | 252 | page = request.GET.get('page') 253 | try: 254 | guests = paginator.page(page) 255 | except PageNotAnInteger: 256 | guests = paginator.page(1) 257 | except EmptyPage: 258 | guests = paginator.page(paginator.num_pages) 259 | 260 | data = { 261 | 'guest_list': guests, 262 | } 263 | return render_to_response('guests.html', 264 | data, 265 | context_instance=RequestContext(request)) 266 | 267 | 268 | def guest_info(request): 269 | if 'name' in request.GET and request.GET['name']: 270 | guest_name = request.GET['name'] 271 | guest = Guest.objects.filter(name=guest_name) 272 | disks = Disk.objects.filter(guest=guest[0]) 273 | vnics = VirtualNic.objects.filter(guest=guest[0]) 274 | data = { 275 | 'guest': guest[0], 276 | 'disks': disks, 277 | 'vnics': vnics, 278 | } 279 | return render_to_response('guest.html', 280 | data, 281 | context_instance=RequestContext(request)) 282 | else: 283 | return HttpResponse('Please submit something !') 284 | 285 | 286 | def hypervisor_info(request): 287 | if 'name' in request.GET and request.GET['name']: 288 | hypervisor_name = request.GET['name'] 289 | hypervisor = Hypervisor.objects.filter(name=hypervisor_name) 290 | guests = Guest.objects.filter(hypervisor=hypervisor[0]) 291 | datastores = Datastore.objects.filter(hypervisor=hypervisor[0]) 292 | interfaces = Interface.objects.filter(hypervisor=hypervisor[0]) 293 | vswitchs = Vswitch.objects.filter(hypervisor=hypervisor[0]) 294 | networks = Network.objects.filter(vswitch__hypervisor=hypervisor[0]) 295 | 296 | data = { 297 | 'hypervisor': hypervisor[0], 298 | 'guests': guests, 299 | 'datastores': datastores.order_by('name'), 300 | 'interfaces': interfaces, 301 | 'vswitchs': vswitchs, 302 | 'networks': networks, 303 | } 304 | return render_to_response('hypervisor.html', 305 | data, 306 | context_instance=RequestContext(request)) 307 | else: 308 | return HttpResponse('Please submit something !') 309 | 310 | 311 | def search(request): 312 | if 'filter' in request.GET and request.GET['filter']: 313 | field = request.GET['filter'] 314 | hypervisor_list = Hypervisor.objects.filter(name__icontains=field) 315 | guest_list = Guest.objects.filter(name__icontains=field) 316 | resourcepool_list = [g.resourcePool for g in Guest.objects.all()] 317 | resourcepool_list = list(set(resourcepool_list)) 318 | 319 | data = [] 320 | for rp in [rp for rp in resourcepool_list if field.lower() in rp.lower()]: 321 | info = resourcepool_info_list(rp) 322 | data.append(info) 323 | 324 | data = { 325 | 'hypervisor_list': hypervisor_list, 326 | 'guest_list': guest_list, 327 | 'resourcepool_list': data, 328 | } 329 | return render_to_response('search.html', 330 | data, 331 | context_instance=RequestContext(request)) 332 | else: 333 | return HttpResponse('Please submit something !') 334 | 335 | 336 | def interface_statistics(hv): 337 | inter_list = Interface.objects.filter( 338 | hypervisor=hv).exclude(vswitch=None) 339 | ret_val = {} 340 | for i in inter_list: 341 | if i.linkSpeed in ret_val: 342 | ret_val[i.linkSpeed] += 1 343 | else: 344 | ret_val[i.linkSpeed] = 1 345 | 346 | return ret_val 347 | 348 | 349 | def sum_list_dict(l): 350 | ret_val = {} 351 | for d in l: 352 | for k, v in d.items(): 353 | if k not in ret_val: 354 | ret_val[k] = v 355 | else: 356 | ret_val[k] += v 357 | return ret_val 358 | 359 | 360 | def datacenter_info(request): 361 | hypervisor_list = Hypervisor.objects.all() 362 | ret_val = {} 363 | for hv in hypervisor_list: 364 | datacenter = hv.datacenter 365 | if datacenter not in ret_val: 366 | ret_val[datacenter] = {'threads': 0, 'vcpu_reserved': 0, 367 | 'memory': 0, 'mem_reserved': 0, 'guest': 0, 368 | 'hypervisor': 0, 'storage': 0, 369 | 'esxiversion': [], 'disk_reserved': 0, 370 | 'linkspeed': [], 'raw': 0} 371 | ret_val[datacenter]['hypervisor'] += 1 372 | ret_val[datacenter]['threads'] += hv.numCpuThreads 373 | ret_val[datacenter]['vcpu_reserved'] += hv.vcpuReserved 374 | ret_val[datacenter]['memory'] += hv.memorySize 375 | ret_val[datacenter]['mem_reserved'] += hv.memoryReserved 376 | ret_val[datacenter]['storage'] += hv.get_datastores_size() 377 | ret_val[datacenter]['disk_reserved'] += hv.diskReserved 378 | ret_val[datacenter]['raw'] += hv.get_raw_disk_reserved() 379 | ret_val[datacenter]['guest'] += hv.get_number_guest() 380 | ret_val[datacenter]['linkspeed'].append(interface_statistics(hv)) 381 | ret_val[datacenter]['esxiversion'].append({str(hv.productVersion): 1}) 382 | 383 | datacenter = [] 384 | for dc in ret_val: 385 | datacenter.append({ 386 | 'datacenter': dc, 387 | 'hypervisor': ret_val[dc]['hypervisor'], 388 | 'storage': ret_val[dc]['storage'], 389 | 'threads': ret_val[dc]['threads'], 390 | 'guest': ret_val[dc]['guest'], 391 | 'vcpu_reserved': ret_val[dc]['vcpu_reserved'], 392 | 'mem_reserved': ret_val[dc]['mem_reserved'], 393 | 'disk_reserved': ret_val[dc]['disk_reserved'], 394 | 'raw': ret_val[dc]['raw'], 395 | 'memory': ret_val[dc]['memory'], 396 | 'linkspeed': reduce(lambda x, y: dict((k, v + y[k]) 397 | for k, v in x.iteritems()), 398 | ret_val[dc]['linkspeed']), 399 | 'esxiversion': sum_list_dict(ret_val[dc]['esxiversion']), 400 | }) 401 | data = { 402 | 'data': datacenter, 403 | } 404 | return data 405 | 406 | 407 | def physical_statistics(request): 408 | data = datacenter_info(request) 409 | data['esxiversion'] = sum_list_dict([x['esxiversion'] for x in data['data']]) 410 | data['linkspeed'] = sum_list_dict([x['linkspeed'] for x in data['data']]) 411 | data['is_raw'] = False 412 | for x in data['data']: 413 | if x.get('raw') > 0: 414 | data['is_raw'] = True 415 | 416 | return render_to_response('physical.html', data, 417 | context_instance=RequestContext(request)) 418 | 419 | 420 | def datacenter_statistics(request): 421 | data = datacenter_info(request) 422 | guest_list = Guest.objects.all() 423 | 424 | guest_rp_dc = {} 425 | for guest in guest_list: 426 | rp = guest.resourcePool 427 | dc = guest.hypervisor.datacenter 428 | if dc not in guest_rp_dc.keys(): 429 | guest_rp_dc[dc] = {} 430 | if rp not in guest_rp_dc[dc]: 431 | guest_rp_dc[dc][rp] = 0 432 | 433 | guest_rp_dc[dc][rp] += 1 434 | u = [] 435 | for dc in guest_rp_dc: 436 | temp = guest_rp_dc[dc] 437 | temp['name'] = dc 438 | u.append(temp) 439 | data['guest_rp_dc'] = u 440 | return render_to_response('datacenter.html', data, 441 | context_instance=RequestContext(request)) 442 | --------------------------------------------------------------------------------