Add an interactive CLI for browsing and rebuilding appliance VMs
(cherry picked from commit 7d8e569999907209e114a10064f97ad528c0b668)
This commit is contained in:
parent
cb8a781c86
commit
272a42ada8
@ -66,13 +66,14 @@ class MissingIPAllocation(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class Router(object):
|
class Router(object):
|
||||||
def __init__(self, id_, tenant_id, name, admin_state_up,
|
def __init__(self, id_, tenant_id, name, admin_state_up, status,
|
||||||
external_port=None, internal_ports=None,
|
external_port=None, internal_ports=None,
|
||||||
management_port=None, floating_ips=None):
|
management_port=None, floating_ips=None):
|
||||||
self.id = id_
|
self.id = id_
|
||||||
self.tenant_id = tenant_id
|
self.tenant_id = tenant_id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.admin_state_up = admin_state_up
|
self.admin_state_up = admin_state_up
|
||||||
|
self.status = status
|
||||||
self.external_port = external_port
|
self.external_port = external_port
|
||||||
self.management_port = management_port
|
self.management_port = management_port
|
||||||
self.internal_ports = internal_ports or []
|
self.internal_ports = internal_ports or []
|
||||||
@ -111,6 +112,7 @@ class Router(object):
|
|||||||
d['tenant_id'],
|
d['tenant_id'],
|
||||||
d['name'],
|
d['name'],
|
||||||
d['admin_state_up'],
|
d['admin_state_up'],
|
||||||
|
d['status'],
|
||||||
external_port,
|
external_port,
|
||||||
internal_ports,
|
internal_ports,
|
||||||
management_port,
|
management_port,
|
||||||
|
129
akanda/rug/cli/browse.py
Normal file
129
akanda/rug/cli/browse.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
# Copyright 2014 DreamHost, LLC
|
||||||
|
#
|
||||||
|
# Author: DreamHost, LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
"""Interactive CLI for rebuilding routers
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from akanda.rug import commands
|
||||||
|
from akanda.rug.api import nova as nova_api
|
||||||
|
from akanda.rug.api import quantum as quantum_api
|
||||||
|
from akanda.rug.cli import message
|
||||||
|
from blessed import Terminal
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
|
||||||
|
class BrowseRouters(message.MessageSending):
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def __init__(self, *a, **kw):
|
||||||
|
self.term = Terminal()
|
||||||
|
self.nova = nova_api.Nova(cfg.CONF)
|
||||||
|
self.quantum = quantum_api.Quantum(cfg.CONF)
|
||||||
|
self.position = 0
|
||||||
|
super(BrowseRouters, self).__init__(*a, **kw)
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
with self.term.fullscreen():
|
||||||
|
with self.term.cbreak():
|
||||||
|
val = None
|
||||||
|
while val != u'q':
|
||||||
|
if not val:
|
||||||
|
self.fetch_routers()
|
||||||
|
elif val.is_sequence:
|
||||||
|
if val.code == self.term.KEY_DOWN:
|
||||||
|
self.move_down()
|
||||||
|
if val.code == self.term.KEY_UP:
|
||||||
|
self.move_up()
|
||||||
|
elif val == u'j':
|
||||||
|
self.move_down()
|
||||||
|
elif val == u'k':
|
||||||
|
self.move_up()
|
||||||
|
elif val == u'r':
|
||||||
|
self.reload_router()
|
||||||
|
self.fetch_routers()
|
||||||
|
self.print_routers()
|
||||||
|
val = self.term.inkey(timeout=5)
|
||||||
|
|
||||||
|
def fetch_routers(self):
|
||||||
|
self.routers = self.quantum.get_routers()
|
||||||
|
for router in self.routers:
|
||||||
|
instance = self.nova.get_instance(router)
|
||||||
|
if instance and instance.image:
|
||||||
|
image = self.nova.client.images.get(instance.image['id'])
|
||||||
|
status = self.term.red('OUT-OF-DATE')
|
||||||
|
if image.id == cfg.CONF.router_image_uuid:
|
||||||
|
status = self.term.green('LATEST')
|
||||||
|
name = status.ljust(11) + ' ' + image.name
|
||||||
|
setattr(
|
||||||
|
router,
|
||||||
|
'image',
|
||||||
|
name
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
setattr(router, 'image', '<no vm>')
|
||||||
|
|
||||||
|
def print_routers(self):
|
||||||
|
visible_height = self.term.height - 2
|
||||||
|
offset = 0
|
||||||
|
if len(self.routers) > visible_height:
|
||||||
|
offset = self.position
|
||||||
|
offset = min(offset, len(self.routers) - visible_height - 1)
|
||||||
|
routers = self.routers[offset:]
|
||||||
|
with self.term.location():
|
||||||
|
for i, r in enumerate(routers):
|
||||||
|
if i > visible_height:
|
||||||
|
continue
|
||||||
|
formatter = lambda x: x
|
||||||
|
args = [
|
||||||
|
r.id,
|
||||||
|
r.name,
|
||||||
|
self.router_states[r.status](r.status.ljust(6)),
|
||||||
|
r.image
|
||||||
|
]
|
||||||
|
if i + offset == self.position:
|
||||||
|
formatter = self.term.reverse
|
||||||
|
print self.term.move(i, 0) + formatter(' '.join(args)).ljust(
|
||||||
|
self.term.width
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_message(self, router):
|
||||||
|
return {
|
||||||
|
'command': commands.ROUTER_UPDATE,
|
||||||
|
'router_id': router.id,
|
||||||
|
'tenant_id': router.tenant_id
|
||||||
|
}
|
||||||
|
|
||||||
|
def reload_router(self):
|
||||||
|
router = self.routers[self.position]
|
||||||
|
self.send_message(self.make_message(router))
|
||||||
|
|
||||||
|
def move_up(self):
|
||||||
|
self.position = max(0, self.position-1)
|
||||||
|
|
||||||
|
def move_down(self):
|
||||||
|
self.position = min(len(self.routers)-1, self.position+1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def router_states(self):
|
||||||
|
return {
|
||||||
|
'ACTIVE': self.term.green,
|
||||||
|
'BUILD': self.term.yellow,
|
||||||
|
'DOWN': self.term.red,
|
||||||
|
'ERROR': self.term.red
|
||||||
|
}
|
@ -43,7 +43,9 @@ class MessageSending(command.Command):
|
|||||||
self.app.rug_ini.amqp_url,
|
self.app.rug_ini.amqp_url,
|
||||||
self.app.rug_ini.outgoing_notifications_exchange,
|
self.app.rug_ini.outgoing_notifications_exchange,
|
||||||
)
|
)
|
||||||
payload = self.make_message(parsed_args)
|
self.send_message(self.make_message(parsed_args))
|
||||||
|
|
||||||
|
def send_message(self, payload):
|
||||||
msg = {
|
msg = {
|
||||||
'event_type': 'akanda.rug.command',
|
'event_type': 'akanda.rug.command',
|
||||||
'payload': payload,
|
'payload': payload,
|
||||||
|
@ -6,3 +6,4 @@ kombu>=2.4.8
|
|||||||
WebOb>=1.2.3,<1.3
|
WebOb>=1.2.3,<1.3
|
||||||
python-novaclient>=2.15.0
|
python-novaclient>=2.15.0
|
||||||
cliff>=1.4.3
|
cliff>=1.4.3
|
||||||
|
blessed>=1.9.1
|
||||||
|
@ -43,6 +43,7 @@ akanda.rug.cli =
|
|||||||
tenant debug=akanda.rug.cli.tenant:TenantDebug
|
tenant debug=akanda.rug.cli.tenant:TenantDebug
|
||||||
tenant manage=akanda.rug.cli.tenant:TenantManage
|
tenant manage=akanda.rug.cli.tenant:TenantManage
|
||||||
workers debug=akanda.rug.cli.worker:WorkerDebug
|
workers debug=akanda.rug.cli.worker:WorkerDebug
|
||||||
|
interactive=akanda.rug.cli.rebuild:RebuildRouters
|
||||||
poll=akanda.rug.cli.poll:Poll
|
poll=akanda.rug.cli.poll:Poll
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
|
Loading…
Reference in New Issue
Block a user