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