Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / tools / add_unique_name_to_nics @ a6e6fe48

History | View | Annotate | Download (6.5 kB)

1 88d998b9 Christos Stavrakakis
#!/usr/bin/env python
2 88d998b9 Christos Stavrakakis
"""Tool to update Ganeti instances:
3 88d998b9 Christos Stavrakakis
4 88d998b9 Christos Stavrakakis
* add unique name to the NICs of all Ganeti instances
5 88d998b9 Christos Stavrakakis
* rename all instance tags related with network firewall profiles to include
6 88d998b9 Christos Stavrakakis
  the unique name of the corresponding NIC.
7 88d998b9 Christos Stavrakakis
8 88d998b9 Christos Stavrakakis
The name for each NIC is based on the PK of the NIC in Cyclades DB.
9 88d998b9 Christos Stavrakakis
"""
10 88d998b9 Christos Stavrakakis
11 88d998b9 Christos Stavrakakis
FIREWALL_TAGS_PREFIX = "synnefo:network:"
12 88d998b9 Christos Stavrakakis
FIREWALL_TAGS = {"ENABLED": "synnefo:network:%s:protected",
13 88d998b9 Christos Stavrakakis
                 "DISABLED": "synnefo:network:%s:unprotected",
14 88d998b9 Christos Stavrakakis
                 "PROTECTED": "synnefo:network:%s:limited"}
15 88d998b9 Christos Stavrakakis
16 88d998b9 Christos Stavrakakis
# Gevent patching
17 88d998b9 Christos Stavrakakis
import gevent
18 88d998b9 Christos Stavrakakis
from gevent import monkey
19 88d998b9 Christos Stavrakakis
monkey.patch_all()
20 88d998b9 Christos Stavrakakis
21 88d998b9 Christos Stavrakakis
import sys
22 88d998b9 Christos Stavrakakis
import subprocess
23 88d998b9 Christos Stavrakakis
from optparse import OptionParser, TitledHelpFormatter
24 88d998b9 Christos Stavrakakis
25 88d998b9 Christos Stavrakakis
# Configure Django env
26 88d998b9 Christos Stavrakakis
from synnefo import settings
27 88d998b9 Christos Stavrakakis
from django.core.management import setup_environ
28 88d998b9 Christos Stavrakakis
setup_environ(settings)
29 88d998b9 Christos Stavrakakis
30 88d998b9 Christos Stavrakakis
from django.db import close_connection
31 88d998b9 Christos Stavrakakis
from synnefo.db.models import Backend, pooled_rapi_client
32 a6e6fe48 Christos Stavrakakis
from synnefo.management.common import get_resource
33 88d998b9 Christos Stavrakakis
34 88d998b9 Christos Stavrakakis
import logging
35 88d998b9 Christos Stavrakakis
logger = logging.getLogger("migrate_nics")
36 88d998b9 Christos Stavrakakis
handler = logging.StreamHandler()
37 88d998b9 Christos Stavrakakis
38 88d998b9 Christos Stavrakakis
formatter = logging.Formatter("[%(levelname)s] %(message)s")
39 88d998b9 Christos Stavrakakis
handler.setFormatter(formatter)
40 88d998b9 Christos Stavrakakis
logger.setLevel(logging.DEBUG)
41 88d998b9 Christos Stavrakakis
logger.addHandler(handler)
42 88d998b9 Christos Stavrakakis
logger.propagate = False
43 88d998b9 Christos Stavrakakis
44 88d998b9 Christos Stavrakakis
DESCRIPTION = """\
45 88d998b9 Christos Stavrakakis
Tool to update all Ganeti instances in order to add a unique name to NICs of
46 88d998b9 Christos Stavrakakis
all instances and rename the instance firewall tags to include the NIC name.
47 88d998b9 Christos Stavrakakis
"""
48 88d998b9 Christos Stavrakakis
49 88d998b9 Christos Stavrakakis
50 88d998b9 Christos Stavrakakis
def main():
51 88d998b9 Christos Stavrakakis
    parser = OptionParser(description=DESCRIPTION,
52 88d998b9 Christos Stavrakakis
                          formatter=TitledHelpFormatter())
53 88d998b9 Christos Stavrakakis
    parser.add_option("--backend-id", dest="backend_id",
54 88d998b9 Christos Stavrakakis
                      help="Update instances only of this Ganeti backend."),
55 88d998b9 Christos Stavrakakis
    parser.add_option("--dry-run", dest="dry_run", default=False,
56 88d998b9 Christos Stavrakakis
                      action="store_true",
57 88d998b9 Christos Stavrakakis
                      help="Do not send any jobs to Ganeti backend.")
58 88d998b9 Christos Stavrakakis
    parser.add_option("--ganeti-dry-run", dest="ganeti_dry_run", default=False,
59 88d998b9 Christos Stavrakakis
                      action="store_true",
60 88d998b9 Christos Stavrakakis
                      help="Pass --dry-run option to Ganeti jobs.")
61 88d998b9 Christos Stavrakakis
    parser.add_option("--parallel", dest="parallel", default=False,
62 88d998b9 Christos Stavrakakis
                      action="store_true",
63 88d998b9 Christos Stavrakakis
                      help="Use a seperate process for each backend.")
64 88d998b9 Christos Stavrakakis
    parser.add_option("-d", "--debug", dest="debug", default=False,
65 88d998b9 Christos Stavrakakis
                      action="store_true",
66 88d998b9 Christos Stavrakakis
                      help="Display debug information.")
67 88d998b9 Christos Stavrakakis
    options, args = parser.parse_args()
68 88d998b9 Christos Stavrakakis
69 88d998b9 Christos Stavrakakis
    if options.backend_id:
70 a6e6fe48 Christos Stavrakakis
        backends = [get_resource("backend", options.backend_id)]
71 88d998b9 Christos Stavrakakis
    else:
72 88d998b9 Christos Stavrakakis
        if Backend.objects.filter(offline=True).exists():
73 88d998b9 Christos Stavrakakis
            msg = "Can not update intances. An 'offline' backend exists."
74 88d998b9 Christos Stavrakakis
            raise Exception(msg)
75 88d998b9 Christos Stavrakakis
        backends = Backend.objects.all()
76 88d998b9 Christos Stavrakakis
77 88d998b9 Christos Stavrakakis
    if options.debug:
78 88d998b9 Christos Stavrakakis
        logger.setLevel(logging.DEBUG)
79 88d998b9 Christos Stavrakakis
80 88d998b9 Christos Stavrakakis
    if len(backends) > 1 and options.parallel:
81 88d998b9 Christos Stavrakakis
        cmd = sys.argv
82 88d998b9 Christos Stavrakakis
        processes = []
83 88d998b9 Christos Stavrakakis
        for backend in backends:
84 88d998b9 Christos Stavrakakis
            p = subprocess.Popen(cmd + ["--backend-id=%s" % backend.id])
85 88d998b9 Christos Stavrakakis
            processes.append(p)
86 88d998b9 Christos Stavrakakis
        for p in processes:
87 88d998b9 Christos Stavrakakis
            p.wait()
88 88d998b9 Christos Stavrakakis
        return
89 88d998b9 Christos Stavrakakis
    else:
90 88d998b9 Christos Stavrakakis
        [upgrade_backend(b, options.dry_run, options.ganeti_dry_run)
91 88d998b9 Christos Stavrakakis
         for b in backends]
92 88d998b9 Christos Stavrakakis
    return
93 88d998b9 Christos Stavrakakis
94 88d998b9 Christos Stavrakakis
95 88d998b9 Christos Stavrakakis
def upgrade_backend(backend, dry_run, ganeti_dry_run):
96 88d998b9 Christos Stavrakakis
    jobs = []
97 88d998b9 Christos Stavrakakis
    instances_ids = get_instances_with_anonymous_nics(backend)
98 88d998b9 Christos Stavrakakis
    for vm in backend.virtual_machines.filter(id__in=instances_ids):
99 88d998b9 Christos Stavrakakis
        jobs.append(gevent.spawn(upgrade_vm, vm, dry_run, ganeti_dry_run))
100 88d998b9 Christos Stavrakakis
101 88d998b9 Christos Stavrakakis
    if jobs:
102 88d998b9 Christos Stavrakakis
        for job_chunk in [jobs[x:x+25] for x in range(0, len(jobs), 25)]:
103 88d998b9 Christos Stavrakakis
            gevent.joinall(jobs)
104 88d998b9 Christos Stavrakakis
    else:
105 88d998b9 Christos Stavrakakis
        logger.info("No anonymous NICs in backend '%s'. Nothing to do!",
106 88d998b9 Christos Stavrakakis
                    backend.clustername)
107 88d998b9 Christos Stavrakakis
    return
108 88d998b9 Christos Stavrakakis
109 88d998b9 Christos Stavrakakis
110 88d998b9 Christos Stavrakakis
def get_instances_with_anonymous_nics(backend):
111 88d998b9 Christos Stavrakakis
    """Get all Ganeti instances that have NICs without names."""
112 88d998b9 Christos Stavrakakis
    with pooled_rapi_client(backend) as rc:
113 88d998b9 Christos Stavrakakis
        instances = rc.GetInstances(bulk=True)
114 88d998b9 Christos Stavrakakis
    # Filter snf- instances
115 88d998b9 Christos Stavrakakis
    instances = filter(lambda i:
116 88d998b9 Christos Stavrakakis
                       i["name"].startswith(settings.BACKEND_PREFIX_ID),
117 88d998b9 Christos Stavrakakis
                       instances)
118 88d998b9 Christos Stavrakakis
    # Filter instances with anonymous NICs
119 88d998b9 Christos Stavrakakis
    instances = filter(lambda i: None in i["nic.names"], instances)
120 88d998b9 Christos Stavrakakis
    # Get IDs of those instances
121 88d998b9 Christos Stavrakakis
    instances_ids = map(lambda i:
122 88d998b9 Christos Stavrakakis
                        i["name"].replace(settings.BACKEND_PREFIX_ID, "", 1),
123 88d998b9 Christos Stavrakakis
                        instances)
124 88d998b9 Christos Stavrakakis
    return instances_ids
125 88d998b9 Christos Stavrakakis
126 88d998b9 Christos Stavrakakis
127 88d998b9 Christos Stavrakakis
def upgrade_vm(vm, dry_run, ganeti_dry_run):
128 88d998b9 Christos Stavrakakis
    """Add names to Ganeti NICs and update firewall Tags."""
129 88d998b9 Christos Stavrakakis
    logger.info("Updating NICs of instance %s" % vm.backend_vm_id)
130 88d998b9 Christos Stavrakakis
    index_to_uuid = {}
131 88d998b9 Christos Stavrakakis
    new_tags = []
132 88d998b9 Christos Stavrakakis
    # Compute new NICs names and firewall tags
133 88d998b9 Christos Stavrakakis
    for nic in vm.nics.all():
134 88d998b9 Christos Stavrakakis
        if nic.index is None:
135 88d998b9 Christos Stavrakakis
            msg = ("Cannot update NIC '%s'. The index of the NIC is unknown."
136 88d998b9 Christos Stavrakakis
                   " Please run snf-manage reconcile-servers --fix-all and"
137 88d998b9 Christos Stavrakakis
                   " retry!")
138 88d998b9 Christos Stavrakakis
            logger.critical(msg)
139 88d998b9 Christos Stavrakakis
            continue
140 88d998b9 Christos Stavrakakis
        uuid = nic.backend_uuid
141 88d998b9 Christos Stavrakakis
        # Map index -> UUID
142 88d998b9 Christos Stavrakakis
        index_to_uuid[nic.index] = uuid
143 88d998b9 Christos Stavrakakis
144 88d998b9 Christos Stavrakakis
        # New firewall TAG with UUID
145 88d998b9 Christos Stavrakakis
        firewall_profile = nic.firewall_profile
146 88d998b9 Christos Stavrakakis
        if firewall_profile and firewall_profile != "DISABLED":
147 88d998b9 Christos Stavrakakis
            firewall_tag = FIREWALL_TAGS[nic.firewall_profile] % uuid
148 88d998b9 Christos Stavrakakis
            new_tags.append(firewall_tag)
149 88d998b9 Christos Stavrakakis
150 88d998b9 Christos Stavrakakis
    renamed_nics = [("modify", index, {"name": name})
151 88d998b9 Christos Stavrakakis
                    for index, name in index_to_uuid.items()]
152 88d998b9 Christos Stavrakakis
153 88d998b9 Christos Stavrakakis
    instance = vm.backend_vm_id
154 88d998b9 Christos Stavrakakis
    with pooled_rapi_client(vm) as rc:
155 88d998b9 Christos Stavrakakis
        # Delete old Tags
156 88d998b9 Christos Stavrakakis
        tags = rc.GetInstanceTags(instance)
157 88d998b9 Christos Stavrakakis
        delete_tags = [t for t in tags if t.startswith(FIREWALL_TAGS_PREFIX)]
158 88d998b9 Christos Stavrakakis
        if delete_tags:
159 88d998b9 Christos Stavrakakis
            logger.debug("Deleting tags '%s' from instance '%s'",
160 88d998b9 Christos Stavrakakis
                         delete_tags, vm.backend_vm_id)
161 88d998b9 Christos Stavrakakis
            if not dry_run:
162 88d998b9 Christos Stavrakakis
                rc.DeleteInstanceTags(instance, delete_tags,
163 88d998b9 Christos Stavrakakis
                                      dry_run=ganeti_dry_run)
164 88d998b9 Christos Stavrakakis
165 88d998b9 Christos Stavrakakis
        # Add new Tags
166 88d998b9 Christos Stavrakakis
        if new_tags:
167 88d998b9 Christos Stavrakakis
            logger.debug("Adding new tags '%s' to instance '%s'",
168 88d998b9 Christos Stavrakakis
                         new_tags, vm.backend_vm_id)
169 88d998b9 Christos Stavrakakis
            if not dry_run:
170 88d998b9 Christos Stavrakakis
                rc.AddInstanceTags(instance, new_tags, dry_run=ganeti_dry_run)
171 88d998b9 Christos Stavrakakis
172 88d998b9 Christos Stavrakakis
        # Add names to NICs
173 88d998b9 Christos Stavrakakis
        logger.debug("Modifying NICs of instance '%s'. New NICs: '%s'",
174 88d998b9 Christos Stavrakakis
                     vm.backend_vm_id, renamed_nics)
175 88d998b9 Christos Stavrakakis
        if not dry_run:
176 88d998b9 Christos Stavrakakis
            rc.ModifyInstance(vm.backend_vm_id,
177 88d998b9 Christos Stavrakakis
                              nics=renamed_nics, dry_run=ganeti_dry_run)
178 88d998b9 Christos Stavrakakis
    close_connection()
179 88d998b9 Christos Stavrakakis
180 88d998b9 Christos Stavrakakis
181 88d998b9 Christos Stavrakakis
if __name__ == "__main__":
182 88d998b9 Christos Stavrakakis
    main()
183 88d998b9 Christos Stavrakakis
    sys.exit(0)